wait() - 在java中以奇怪的方式发生故障的notify()机制

时间:2015-07-28 17:21:13

标签: java multithreading synchronization producer-consumer

我尝试在这里阅读类似问题的一些答案(我总是这样做),但没有找到(或者不理解?)这个特定问题的答案。

我正在实现一个相当简单的消费者生产者类,它从不同的线程接收列表中的元素并重复使用它们。该类具有以下代码:

public class ProduceConsume implements Runnable
{

    LinkedList<Integer> _list = new LinkedList<Integer>();

    public synchronized void produce(Integer i)
    {
        _list.add(i);
        notify();
    }

    public void run()
    {
        while(true)
        {
            Integer i = consume();

            // Do something with the integer...
        }
    }

    private synchronized Integer consume()
    {
        if(_list.size() == 0)
        {
            try
            {
                wait();
            }
            catch(InterruptedException e){}

            return _list.poll();
        }
    }
}

问题是 - 它通常可以正常工作,但有时,执行到达

return _list.poll();

列表仍为空。我无法绕过它 - 我做的事情非常糟糕吗?不应该重复尝试轮询的可运行线程检测零长度列表,等待,并且只有在生成器方法完成后才被唤醒,因此使列表非空?

除了要求制作之外,没有别的东西从外面“接触”这个类。在runnable类上没有同步其他线程。

顺便说一句,由于几个原因,我希望使用我自己的变体,而不是像CopyOnWriteArrayList等类。

谢谢!任何帮助将不胜感激。

P.S - 我没有多次使用等待通知,但是当我这样做时,在过去,它有效。所以如果我道歉,如果我犯了一些巨大的愚蠢错误!

2 个答案:

答案 0 :(得分:2)

由于等待释放锁定,因此无法在开始等待之前根据测试条件进行推理,假设条件必须在等待退出后必须更改无效。你需要在循环中调用wait,这样一旦线程停止等待并再次获取锁定,它就会检查它正在等待的条件是否具有预期值:

private synchronized Integer consume()
{
    try {
        while (_list.size() == 0) {
            wait();
        }            
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
    return _list.poll();
}

来自the Oracle tutorial

  

注意:始终在测试等待条件的循环内调用wait。

同样不能安全地假设只是因为等待返回了某些内容发送了通知。即使没有通知(虚假唤醒),等待也可以返回。

如果没有一个完整的工作示例,很难说是什么导致了你所看到的。

链接的Oracle教程页面包含您可能想要查看的Producer Consumer示例。

答案 1 :(得分:2)

作为Object.wait州的Javadoc

  

在一个参数版本中,中断和虚假唤醒是可能的,并且此方法应始终在循环中使用:

 synchronized (obj) {
     while (<condition does not hold>)
         obj.wait();
     ... // Perform action appropriate to condition
 }

此外,您不应忽略InterruptedException之类的异常。这看起来像是一个虚假的唤醒,正如你所说的产生错误。

private synchronized Integer consume() {
    try {
        while (_list.isEmpty()) 
            wait();
        return _list.poll();
    } catch(InterruptedException e) {
        throw new IllegalStateException("Interrupted");
    }
}