使用wait和notify终止Producer使用者设置

时间:2014-12-04 08:46:45

标签: java multithreading concurrency thread-safety producer-consumer

请在下面找到消费者生产商代码:

//制片人

    public boolean busy = false;
    while (rst != null && rst.next()) {
        while (queue.size() == 10) {
            synchronized (queue) {
                try {
                    log.debug("Queue is full, waiting");
                    queue.wait();
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
            }
        }

        synchronized (queue) {
            queue.add(/* add item to queue */);
            busy = true;
            queue.notifyAll();
        }
    }

//消费者

    try {
        while (!busy) {
            while (queue.isEmpty()) {
                synchronized (queue) {
                    try {
                        log.debug("Queue is empty, waiting");
                        queue.wait();
                    } catch (InterruptedException ex) {
                        ex.getMessage();
                    }
                }
            }

            synchronized (queue) {
                item = queue.remove();
                log.debug("Consumed item" + ++count + " :" + item);
                busy = false;
                queue.notifyAll();
            }
        }

在我的生产者代码片段中,我已经同步了队列(链接阻塞队列),我用它来添加元素并使全局布尔变量busy为true并通知消费者。一旦队列大小为10,生产者就会释放对象锁并进入等待状态。 对于我的消费者,一旦全局标志忙碌,消费者就会消耗元素并将标志变为假。 这些元素是正确生成和使用的。但是我的代码并没有终止于生产者消费者循环之外,要执行的最终语句是“队列是空的,等待”。 请让我知道如何修改我的代码和终止条件以退出循环。

1 个答案:

答案 0 :(得分:4)

您的代码被破坏的方式很多,很难决定,从哪里开始。首先,您的消费者完全包含在while(!busy){循环中,因此一旦busy变为true,它将退出循环,而不是开始使用项目。但是,由于您在任何同步机制之外访问busy变量,因此无法保证此线程将看到另一个线程写入的true值。此时,它“帮助”消费者在wait返回时没有真正检查队列的状态,而只是假设有些项目(不保证,读取“spurios wakeups”等)。{{ {3}}解释了如何正确等待。

但是还有更多错误无法正确同步。在你说的制作人

while(queue.size() == 10){
    synchronized(queue){
        …

换句话说,您正在通过调用size()而没有正确同步来访问队列。

另一个问题是您的异常处理:

catch(InterruptedException ex)
{
    ex.getMessage();
}

调用getMessage但忽略结果就像根本不调用它一样。你应该在这里添加一个真实的动作。即使是简单的print语句也比这更好。

此外,将等待部分和生产/消费部分拆分为两个不同的synchronized块可能会有效,只要您有一个生产者和一个消费者,但只要您有更多的线程,它就会被打破,因为在在这两个synchronized块之间,队列的状态可能会再次发生变化。


正确地做到这一点并不困难,只需简化您的代码:

//Producer
public volatile boolean producerDone = false;
while( /* can produce more items */ ) {
    Item item=produceItem();
    synchronized(queue) {
        while(queue.size() == 10) try {
           log.debug("Queue is full, waiting");
           queue.wait();
        } catch(InterruptedException ex) {
            ex.printStackTrace();
        }
        queue.add(item);
        queue.notifyAll();
    }
}
producerDone=true;

//Consumer
while(!producerDone) {
    Item item;
    synchronized(queue) {
        while(queue.isEmpty()) try {
            log.debug("Queue is empty, waiting");
            queue.wait();
        } catch(InterruptedException ex) {
            ex.printStackTrace();
        }
        item = queue.remove();
        queue.notifyAll();
    }
    processItem(item);
}