多线程-生产者消费者(synchronized同步)
正解博客:https://blog.csdn.net/u011863767/article/details/59731447
永远在循环(loop)里调用 wait 和 notify,不是在 If 语句
现在你知道wait应该永远在被synchronized的背景下和那个被多线程共享的对象上调用,下一个一定要记住的问题就是,你应该永远在while循环,而不是if语句中调用wait。因为线程是在某些条件下等待的——在我们的例子里,即“如果缓冲区队列是满的话,那么生产者线程应该等待”,你可能直觉就会写一个if语句。但if语句存在一些微妙的小问题,导致即使条件没被满足,你的线程你也有可能被错误地唤醒。所以如果你不在线程被唤醒后再次使用while循环检查唤醒条件是否被满足,你的程序就有可能会出错——例如在缓冲区为满的时候生产者继续生成数据,或者缓冲区为空的时候消费者开始小号数据。所以记住,永远在while循环而不是if语句中使用wait!我会推荐阅读《Effective Java》,这是关于如何正确使用wait和notify的最好的参考资料。
有的这样说:(http://www.tuicool.com/articles/a6ram23)
因为在多核处理器环境中, Signal 唤醒操作可能会激活多于一个线程(阻塞在条件变量上的线程),使得多个调用等待的线程返回。所以用while循环对condition多次判断,可以避免这种假唤醒。
基于以上认知,下面这个是使用wait和notify函数的规范代码模板:
1
2
3
4
5
6
7
8
|
// The standard idiom for calling the wait method in Java synchronized (sharedObject) { while (condition) { sharedObject.wait(); // (Releases lock, and reacquires on wakeup) } // do action based upon condition e.g. take or put into queue } |
就像我之前说的一样,在while循环里使用wait的目的,是在线程被唤醒的前后都持续检查条件是否被满足。如果条件并未改变,wait被调用之前notify的唤醒通知就来了,那么这个线程并不能保证被唤醒,有可能会导致死锁问题。
注意:
1 永远在synchronized的方法或对象里使用wait、notify和notifyAll,不然Java虚拟机会生成 IllegalMonitorStateException。
2 永远在while循环里而不是if语句下使用wait。这样,循环会在线程睡眠前后都检查wait的条件,并在条件实际上并未改变的情况下处理唤醒通知。
3 永远在多线程间共享的对象(在生产者消费者模型里即缓冲区队列)上使用wait。
生产者消费者代码:
https://zhuanlan.zhihu.com/p/20300609 代码有部分问题,修改如下
ProsumerToConsumer类
public class ProsumerToConsumer { public static void main(String[] args) throws Exception { Person person =new Person(); Thread t1=new Thread(new Producer(person),"生产者t1"); Thread t2=new Thread(new Producer(person),"生产者t2"); Thread t3=new Thread(new Producer(person),"生产者t3"); Thread s1=new Thread(new Consumer(person), "消费者s1"); Thread s2=new Thread(new Consumer(person), "消费者s2"); Thread s3=new Thread(new Consumer(person), "消费者s3"); s1.start(); s2.start(); Thread.sleep(2000); t1.start(); t2.start(); t3.start(); } }
producer代码:
public class Producer implements Runnable { private Person person; // private String name; public Producer( Person person) { this.person=person; // this.name=name; } @Override public void run() { try { person.producer(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
consumer代码:
public class Consumer implements Runnable{ private Person person; public Consumer( Person person) { this.person=person; } @Override public void run() { try { person.consumer(); } catch (Exception e) { e.printStackTrace(); } } }
person代码:
public class Person { private static volatile int num=0; private Object obj= new Object(); private static final int MAX_NUM=5; public void producer()throws InterruptedException{ while(true){ synchronized (obj) { while(num==MAX_NUM) { System.out.println("box is full,size = " + num); obj.wait(); } num++; System.out.println( Thread.currentThread().getName()+ num); obj.notifyAll(); } } } public void consumer() throws InterruptedException{ while (true) { synchronized (obj) { while (num==0) { System.out.println("box is empty,size = " + num); obj.wait(); } num--; obj.notifyAll(); System.out.println(Thread.currentThread().getName() + num); } } } }
实例验证1:如果判断用的是while 数据在队列容量范围之内。
while(num==MAX_NUM)
while(true){ synchronized (obj) { while(num==MAX_NUM) { System.out.println("box is full,size = " + num); obj.wait(); }
实例验证2:如果判断用的是if ,数据已经超出了队列的容量
if(num==MAX_NUM)
while(true){ synchronized (obj) { if(num==MAX_NUM) { System.out.println("box is full,size = " + num); obj.wait(); }