代码之家  ›  专栏  ›  技术社区  ›  Nishant_Singh

具有多个使用者的生产者使用notify()失败

  •  1
  • Nishant_Singh  · 技术社区  · 7 年前

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

    我的观点是:如果有一个生产者和多个消费者,那么notify()将只调用一个处于等待状态的线程,而rest将保持在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()时失败的原因。

    1 回复  |  直到 7 年前
        1
  •  2
  •   Nathan Hughes    7 年前

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

    如果您查看ArrayBlockingQueue,它是用ReentrantLock实现的,有单独的条件,一个用于消费者,一个用于生产者,这样这种错误就不会发生。