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

问题描述 投票:2回答:2

这个问题在这里已有答案:

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

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

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

java multithreading synchronization producer-consumer
2个回答
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");
    }
}

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示例。

© www.soinside.com 2019 - 2024. All rights reserved.