一个缓冲区类BufferLock采用synchronized同步方法,一个发送线程Sender,一个接收线程Receiver,运行结果都不一致,偶尔才对,问题出在哪里呢?运行结果有时是:
Sender put: 1
 Receiver get: 1
 Receiver get: 2
Sender put: 2
Sender put: 3
 Receiver get: 3
Sender put: 4
 Receiver get: 4
Sender put: 5
 Receiver get: 5public class BufferLock 
{
private int value;
private boolean isEmpty=true;
public synchronized void put(int i)
{
while(!isEmpty)
try
{
this.wait();
}
catch(InterruptedException e){}
value=i;
isEmpty=false;
notify();
}
public synchronized int get()
{
while(isEmpty)
try
{
this.wait();
}
catch(InterruptedException e){}
isEmpty=true;
notify();
return value;
}
}
class Sender extends Thread
{
private BufferLock buffer;
public Sender(BufferLock buffer)
{
this.buffer=buffer;
}
public void run()
{
for(int i=1;i<6;i++)
{
buffer.put(i);
System.out.println("Sender put: "+i);
}
}
}
class Receiver extends Thread
{
private BufferLock buffer;
public Receiver(BufferLock buffer)
{
this.buffer=buffer;
}
public void run()
{
for(int i=1;i<6;i++)
System.out.println("\t\t Receiver get: "+buffer.get());
}
public static void main(String args[])
{
BufferLock buffer=new BufferLock();
(new Sender(buffer)).start();
(new Receiver(buffer)).start();
}
}

解决方案 »

  1.   

    锁共享的对象,也就是“buffer”对象。
    public class BufferLock {
    public static void main(String args[]) {
    BufferLock buffer = new BufferLock();
    (new Sender(buffer)).start();
    (new Receiver(buffer)).start();
    } private int value;
    private boolean isEmpty = true; public void put(int i) {
    while (!isEmpty)
    try {
    this.wait();
    } catch (InterruptedException e) {
    }
    value = i;
    isEmpty = false;
    notify();
    } public int get() {
    while (isEmpty)
    try {
    this.wait();
    } catch (InterruptedException e) {
    }
    isEmpty = true;
    notify();
    return value;
    }
    }class Sender extends Thread {
    private BufferLock buffer; public Sender(BufferLock buffer) {
    this.buffer = buffer;
    } public void run() {
    synchronized (buffer) {
    for (int i = 1; i < 6; i++) {
    buffer.put(i);
    System.out.println("Sender put: " + i);
    }
    }
    }
    }class Receiver extends Thread {
    private BufferLock buffer; public Receiver(BufferLock buffer) {
    this.buffer = buffer;
    } public void run() {
    synchronized (buffer) {
    for (int i = 1; i < 6; i++)
    System.out.println("\t\t Receiver get: " + buffer.get());
    }
    }}
    无论哪个线程锁了buffer,另外一个线程就只能wait了,直到noticy。也就不会出现多次receive或者多次sender的现象了。
      

  2.   


    锁共享的对象确实可以。这个是书上的例子,用synchronized同步方法,我百度了一下:
    synchronized锁定的是哪个对象呢?它锁定的是调用这个同步方法对象。也就是说,当一个对象f1在不同的线程中执行这个同步方法时,它们之间会形成互斥,达到同步的效果。
    new Thread(f1).start();
    new Thread(f1).start();

    为什么这个程序不行呢?
    我有一个疑问:
    public synchronized int get()
        {
            while(isEmpty)
                try
                {
                    this.wait();
                }
                catch(InterruptedException e){}
            isEmpty=true;    
            notify();    //会不会在这句语句执行之后还没return的时候发生线程调度了?
            return value;
        }
      

  3.   

    1、synchronized锁定的是哪个对象呢?这个问题可以看这里:http://arthennala.blog.51cto.com/287631/56356,已经非常详细了。
    2、它锁定的是调用这个同步方法对象。也就是说,当一个对象f1在不同的线程中执行这个同步方法时,它们之间会形成互斥,达到同步的效果。
    new Thread(f1).start();
    new Thread(f1).start();
    为什么这个程序不行呢?
    这个问题,只要关注下new Thread()里的参数啊。new Thread(f1).start();
    new Thread(f1).start();这个是多个线程访问[color=#FF0000]同一个方法,所以锁方法对这两个线程是生效的。[/color],但你这个程序里,不是多个线程调用同一个方法啊,是2个线程都调用了不同的方法啊。
    3、//会不会在这句语句执行之后还没return的时候发生线程调度了?
    看这里http://www.blogjava.net/qileilove/archive/2011/09/22/359262.html。最主要是这句话:当一个被线程被唤醒 (notify)后,才会进入到就绪队列,等待cpu的调度,主要一句话就是:多线程里,CPU的线程调度我们控制不了。
      

  4.   

    你想做一个调度控制?也能实现,放置一个标记变量和一个全局变量比较,什么时候吻合顺序再执行,否则就直接wait notify,直到吻合循序的那个线程出现时为止。
      

  5.   


    两篇文章我都看了,收获很多,不过第一篇文章有提到:
    如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法。
    看起来书上这个例子理论上好像并没有错误,不过我把main方法改成:
            BufferLock buffer=new BufferLock();
            Sender s=new Sender(buffer);
            Receiver r=new Receiver(buffer);
    s.start();
    r.start();
    执行结果要好一点,前面几次都是对的,七八次以后又会出现错误的结果了,不过错误结果的概率小了。
    所以看来还有别的原因?
      

  6.   

    把打印放在put、get方法内部就没问题了,同步锁锁的是get/put方法,而不是run的循环,会有问题。
      

  7.   

    没错,问题解决了,原来的程序在print语句执行之前可能就发生线程调度了。