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

wait() - notify() mechanism in java malfunctioning in a strange way

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

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

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();

列表仍然是空的。我无法理解它——我做错了什么吗?反复尝试轮询检测零长度列表的可运行线程不应该等待,并且仅在生产者方法完成后才被唤醒,从而使列表非空吗?

没有别的 "touches" 来自外部的 class,除了生产调用。 runnable class.

上没有其他线程同步

顺便说一下,出于多种原因,我希望使用自己的变体,而不是 classCopyOnWriteArrayList 等

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

P.S - 我没有多次使用 wait-notify,但是过去,当我使用它时,它起作用了。因此,如果我犯了一些愚蠢的错误,我深表歉意!

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

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

来自the Oracle tutorial

Note: Always invoke wait inside a loop that tests for the condition being waited for.

此外,仅仅因为等待 return 某些东西发送了通知就假设是不安全的。即使没有通知(虚假唤醒)也可以等待 return。

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

链接的 Oracle 教程页面有一个您可能想要查看的生产者消费者示例。

作为 Object.wait 的 Javadoc 状态

As in the one argument version, interrupts and spurious wakeups are possible, and this method should always be used in a loop:

 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");
    }
}