我尝试在这里阅读类似问题的一些答案(我总是这样做),但没有找到(或者不理解?)这个特定问题的答案。
我正在实现一个相当简单的消费者生产者类,它从不同的线程接收列表中的元素并重复使用它们。该类具有以下代码:
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 - 我没有多次使用等待通知,但是当我这样做时,在过去,它有效。所以如果我道歉,如果我犯了一些巨大的愚蠢错误!
答案 0 :(得分:2)
由于等待释放锁定,因此无法在开始等待之前根据测试条件进行推理,假设条件必须在等待退出后必须更改无效。你需要在循环中调用wait,这样一旦线程停止等待并再次获取锁定,它就会检查它正在等待的条件是否具有预期值:
private synchronized Integer consume()
{
try {
while (_list.size() == 0) {
wait();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return _list.poll();
}
注意:始终在测试等待条件的循环内调用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");
}
}