具有多个消费者的生产者因 notify() 而失败

Producer with multiple consumer failing with notify()

我有这段代码,它模拟了在共享 Q class 对象上使用单个生产者和多个消费者的生产者消费者问题。我在这里使用 notify() 而不是 notifyAll() 因为我需要理解为什么这段代码会进入死锁或无限等待状态。

我的观点是:如果有一个生产者和多个消费者,那么 notify() 将只调用一个处于等待状态的线程,其余线程将保持在 wait() 状态。然后生产者将再次恢复,因此代码将继续执行。

观察:这里所有线程生产者和消费者都处于无限等待状态。代码如下所示:

public class ProdConsProb {
    public static void main(String[] args) {
        Q q = new Q();
        Thread producerThread = new Thread(new Producer(q), "producerThread");
        Thread consumerThread = new Thread(new Consumer(q), "Consumer1");
        Thread consumerAnotherThread = new Thread(new Consumer(q), "Consumer2");
        Thread consumerYetAnotherThread = new Thread(new Consumer(q), "Consumer3");
        producerThread.start();
        consumerThread.start();
        consumerAnotherThread.start();
        consumerYetAnotherThread.start();
    }
}

class Producer implements Runnable {
    Q q;

    public Producer(Q q) {
        this.q = q;
    }

    @Override
    public void run() {
        int i = 0;
        while (true)
            try {
                q.setN(i++);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

    }

}

class Consumer implements Runnable {
    Q q;

    public Consumer(Q q) {
        this.q = q;
    }

    @Override
    public void run() {
        while (true)
            try {
                q.getN();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    }

}

class Q {
    private int n = 0;
    boolean valueSet = false;

    public synchronized int getN() throws InterruptedException {
        while (!valueSet) 
        {
            wait();
        }
        valueSet = false;
        notify();
        return n;
    }

    public synchronized void setN(int n) throws InterruptedException {
        while (valueSet == true)
        {
            wait();
        }
        this.n = n;
        valueSet = true;
        notify();

    }
}

我添加了一些系统输出。生成的日志如下所示:

producerThread :: SetN : Valueset is false
producerThread :: Producer inserted 0
producerThread :: SetN : Valueset after is true
producerThread :: SetN : Valueset is true
producerThread wait() ------------ Active 
producerThread :: SetN :Wait() Valueset is true
Consumer3  Start :: GetN : Valueset is true
Consumer3 :: Consumer read 0
Consumer3  End :: GetN : Valueset after is false
Consumer3  Start :: GetN : Valueset is false
Consumer3 wait() ------------ Active 
Consumer3 :: GetN :Wait() Valueset is false
Consumer2  Start :: GetN : Valueset is false
Consumer2 wait() ------------ Active 
Consumer2 :: GetN :Wait() Valueset is false
Consumer1  Start :: GetN : Valueset is false
Consumer1 wait() ------------ Active 
Consumer1 :: GetN :Wait() Valueset is false
producerThread wait()   ------------- left 
producerThread :: Producer inserted 1
producerThread :: SetN : Valueset after is true
producerThread :: SetN : Valueset is true
producerThread wait() ------------ Active 
 -->>   producerThread :: SetN :Wait() Valueset is true
        Consumer3 wait() left 
        Consumer3 :: Consumer read 1
        Consumer3  End :: GetN : Valueset after is false
        Consumer3  Start :: GetN : Valueset is false
        Consumer3 wait() ------------ Active 
        Consumer3 :: GetN :Wait() Valueset is false
 ????   Consumer2 wait() left 
        Consumer2 wait() ------------ Active 
        Consumer2 :: GetN :Wait() Valueset is false

这里比较奇怪的是有一次生产者插入1后通知,消费者3读取数据并通知生产者。现在生产者 3 必须从它的 wait() 触发回来,但是 customer2 线程离开它的 wait() 并返回到 wait() 。

注意:此代码与 notifyAll() 一起使用,但我正在寻找一个原因,说明为什么它与 notify() 一起失败。

它失败了,因为生产者和消费者在同一个监视器上等待并且内部锁不支持单独的条件。如果通知发生,生产者或消费者都可以得到它。但是给定的通知将仅适用于一个或另一个。当一个人收到一个只有另一个人可以执行的通知时,这个通知就没有任何用处:被通知的线程醒来,发现它正在等待的条件仍然为假,然后返回等待。

你看ArrayBlockingQueue,它是用ReentrantLock实现的,条件分开,一个给消费者,一个给生产者,这样就不会出现这种bug。