生产者消费者注意细节

来源:互联网 发布:日本战国 知乎 编辑:程序博客网 时间:2024/06/11 10:25

今天听了马老师的生产者消费者问题,里面提到一些细节,以前写的时候没注意,特地记录一下:

class Pool<T> {private final int MAX = 20;private volatile int top = 0;List<T> list = new ArrayList<>(MAX);public synchronized void put(T e) throws InterruptedException {//第一个要注意的问题,这里要用while进行判断。while(top == MAX) {wait();}list.add(top, e);top++;//第二个问题,这里要用notifyAll。notifyAll();}public synchronized T get() throws InterruptedException {while(top == 0) {wait();}top--;T t = list.get(top);//其实这里有内存泄漏,用remove(top)可能好点。notifyAll();return t;}public int getSize() {return top;}}

以上代码中,要注意的地方有两点:
1.put和get时,判断栈是否为空或是否为满要用while而不能用if
举个反栗,如果使用if进行判断的话:
a:多个生产者要执行put方法,发现Pool已经满了,放不进去了,此时这些生产者均会进入wait状态;
b:此时有一个消费者消费了一个产品,并唤醒了生产者们;
c:生产者1获得锁,向Pool中放入一个产品,top变成了MAX;
d:不巧,生产者2接着获得锁,打算再放入一个产品,即执行list.add(top,e),这下完犊子了吧,放进去的东西超过MAX了。
所以要用while,当一个线程被唤醒后,不会立即对Pool执行修改,而是再次进行检测,某种程度上类似于Singleton的双重检测机制:

public class Singleton {   private volatile static Singleton instance = null;   private Singleton() {}   public static Singleton getInstance() {    if (instance == null) {     synchronized (Singleton.class) {    if (instance == null) {     instance = new Singleton();    }     }    }    return instance;   }  }  

2.要用notifyAll而不是notify
举个反例,用notify而没有用notifyAll
似乎反例不太好举,马老师举得例子不太实际,他说:
消费者全wait了
生产者只有一个醒着的,现在Pool里有MAX-1个产品,那么该生产者生产完放入,唤醒了另一个生产者,
接下来,这俩生产者看到Pool满了,都相继进入wait,结果就是死锁。

我想来想去都想不通他说的这种:消费者都进入wait而生产者竟然没唤醒消费者的青黄是如何发生的。

不管则么说,用notifyAll肯定是最保险的。