package thread;public class ProducerConsumerTest {
public static void main(String[] args) {
Queue queue = new Queue();
Producer p = new Producer(queue);
Consumer c = new Consumer(queue);
p.start();
c.start(); }}class Queue {
private int data;
private boolean isFull = false; public synchronized int get() {
if (!isFull) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
isFull = false;
notify(); return data;
} public synchronized void set(int data) {
if (!isFull) {
this.data = data;
// System.out.println("Producer put data "+data);位置1
isFull = true;
notify();
}
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
} }}class Producer extends Thread {
Queue queue; Producer(Queue queue) {
this.queue = queue;
} public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println("Producer put data " + i);//位置2。把这句代码从上面注释的地方挪到这个地方。
queue.set(i);
}
}
}class Consumer extends Thread {
Queue queue; Consumer(Queue queue) {
this.queue = queue;
} public void run() {
while (true) {
synchronized (queue) {
System.out.println("get data " + queue.get());//3
}
}
}
}//现在的问题是,把位置1的注释掉,使位置2生效后,同样线程同步。疑问是,应该存在这样的可能:在3只执行了queue.get()后,跳出,位置2获得执行,这样应该没有同步啊,也就是,put put 。

解决方案 »

  1.   

    package thread;public class Test { public static void main(String[] args) {
    // TODO Auto-generated method stub
    One one = new One();
    Thread t = new MyTest(one);
    Thread t2 = new MyTest2(one);
    t2.start();
    t.start();
    }}class One {}class MyTest extends Thread {
    One o; public MyTest(One o) {
    this.o = o;
    } public void run() { for (int i = 1; i <= 10; i++) {
    System.out.println(i);
    } }
    }class MyTest2 extends Thread {
    One o; public MyTest2(One o) {
    this.o = o;
    } public void run() {
    synchronized (o) {
    for (int i = 1; i <= 10; i++) {
    System.out.println("syn" + i);
    }
    }
    }
    }
    //在只有一个同步块时,另一个是非synchronizd修饰的run方法时,同步块不是原子操作,可以被其他线程打断。对上面问题的补充说明。
      

  2.   

    LZ想问什么,把1改到2,2没在同步块中,不能保证一致
    简单的说,比如
    synchronized(A) {
        System.out.println("A");
    }
    System.out.println("B"); 
    这两个打印不一定能保证一致,因为线程(姑且叫这个线程为线程1)执行完synchronized块后,刚要打印System.out.println("B");的时候,系统可能把CPU收回,分配给其他线程(线程2线程3线程4等等),其他线程可能也会打印别的东西,过了一段时间线程1又获得CPU权,继续打印System.out.println("B");这样,System.out.println("A");和System.out.println("B");就不能保证是连续的,也就是说从System.out.println("A");到System.out.println("B");不一致,因为中间其他线程可能作了某些操作让结果发生变化了。
      

  3.   

    不知道LZ想要问什么问题,但是同意5楼观点,位置1和2我都没注释,另外我把程序加了句话
    class Queue {
    private int data;
    private boolean isFull = false;public synchronized int get() {
    if (!isFull) {
    try {
    System.out.println("有吗");
    wait();
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    得到结果是:
    Producer put data0
    Producer put data0
    get data0
    有吗?
    Producer put data1
    Producer put data1
    get data1
    有吗?为什么在执行完get data()后它又去执行get data()里面的if语句块了?哪位大虾能否解释一下?谢谢!
      

  4.   

    刚才说错了,不是get data(),是get()
      

  5.   

    LS的问题不就是上面我说的那么个问题吗,线程c执行时,调用queue.get,执行isFull=false;notify();这时p线程被唤醒(只是唤醒,但是不一定能分配到CPU),然后c继续执行打印3,System.out.println("get data " + queue.get());这时候synchronized结束。接下来系统随机分配CPU给p或c,如果此时系统继续分配CPU给c,那么c继续调用queue.get,因为上面知道此时isFull为false,所以会进入if (!isFull),所以就继续打印 “有吗?”
      

  6.   

    首先,谢谢大家。
    阿宝分析的对。
    另外我可能没有把问题表达清楚。补充一下。在位置1时,实现了同步容易理解;但放到位置2时,个人认为不应该实现同步啊(因为在只有一个同步块时,另一个是非synchronizd修饰的run方法时,同步块不是原子操作,可以被其他线程打断,上面有我给的例子),也就是存在这样的可能:在3只执行了queue.get()后,跳出,位置2获得执行,这样应该没有同步啊,也就是,put put 。但运行结果,似乎同样实现了同步。
      

  7.   

    还是不太明白你说的,首先在3只执行了queue.get()后,锁还是被c线程占用着,因为c的run是synchronized(queue),所以必须是执行完整个System.out.println结束退出synchronized块以后,p才有可能获得锁权利而执行
    可以假设一下你的程序执行
    情况1 p先执行,打印2,假如p的cpu没被收回,那么p继续执行queue.set,会打印1,于是就会 put put,然后p发生wait,然后c执行会打印3(上面说了只有打印3结束c才会释放锁),**然后系统随机分配cpu给p或c,我们分a,b两种可能讨论
    a)这时如果cpu分配给c,那么c会发生进入get的if (!isFull)发生wait,然后p执行,打印2,而c此时还在wait,所以p继续执行,打印1,于是有put put,然后唤醒c,p发生wait,然后c执行,会打印3;
    b)如果这时cpu分配给p,那么p会打印2,这时我们分1)和2)两种可能讨论
    1)p的cpu被收回分配给c,然后c执行get,进入if (!isFull)发生wait,然后p执行,打印1,于是发生put put,然后唤醒c,p发生wait,然后c执行,会打印3
    2)p的cpu没被收回,继续打印1,于是有put put,然后唤醒c,p发生wait,然后c执行,会打印3;
    接下来的情况,重复**情况2 p先执行,打印2,假如p的cpu被收回,c执行,那么c调用queue.get会进入if (!isFull)发生wait,于是p执行,打印1,所以就会 put put,然后唤醒c,p发生wait,c执行,会打印3,接下来同情况1的**情况3 c先执行,那么c调用queue.get会进入if (!isFull)发生wait,于是p执行,打印2,这时因为c还在wait中,所以p可以继续执行queue.set,打印1,所以就会 put put,然后唤醒c,p发生wait,c执行,会打印3,接下来的情况同情况1的**所以看上去,打印2好像也发生了同步,其实只是因为线程本身发生了等待而造成的,如果把2语句改在queue.set之后,那么你会发现打印2会发生在打印3之后,所以从结果上看(如果打印1注释掉),会觉得好像先发生了get才发生put,其实从程序上看,这是不可能,因为先get,没有数据会进入wait,但打印结果却是先get后put,这就说明打印2和set方法不同步不知道这么说LZ明白了吗?
      

  8.   

    但是p得run方法没有synchronized啊,只到位置2下面的set时,才有synchronized修饰。在一楼位置,我写了个与synchronized有关的例子,用来说明:在只有一个同步块时,另一个是非synchronizd修饰的run方法时,同步块不是原子操作,可以被其他线程打断。也就是说,在3只执行了queue.get()后,跳出,位置2获得执行。有没有这种可能?
      

  9.   

    我好像明白了,因为c的run是synchronized(queue),即使在synchronized修饰的get方法中有notify,get方法执行完后也没用把锁释放,所以get中的notify方法是无效的。