我有一个带有对象的LinkedList,我想要处理它。对象从另一个线程添加到它,但只有一个线程从它删除/读取。
private LinkedList<MyObject> queue = new LinkedList<>();
new Thread()
{
@Override
public void run()
{
while (!Thread.interrupted())
{
if (!queue.isEmpty())
{
MyObject first = queue.removeFirst();
// do sth..
}
}
}
}.start();
在另一个线程中,我将对象添加到队列
queue.add(new MyObject());
有时这段代码会导致异常,但我无法向自己解释。 线程中的异常&#34;&#34; java.util.NoSuchElementException 在java.util.LinkedList.removeFirst(LinkedList.java:270)
我不知道,为什么我会得到这个例外,因为它只应该尝试删除一个对象(如果存在)。
答案 0 :(得分:2)
正如尼古拉斯已经提到的,你需要一个线程安全的实现。我建议使用LinkedBlockingQueue
。
您可以使用offer
方法添加,并使用take
删除,这也将解决您的“忙碌等待”问题。
答案 1 :(得分:0)
LinkedList
不是线程安全的,所以你不能像现在这样分享几个线程,否则你将面临像这样的不可预测的错误,因为并发修改导致在不一致的状态下,请使用线程安全的双端,例如ConcurrentLinkedDeque
。
答案 2 :(得分:0)
虽然我认为已经提供了一些关于如何解决问题的好解决方案,但没有一个答案解释了为什么@BluE会看到NoSuchElementException。所以这就是我认为可能发生的事情。
由于LinkedList访问未同步,因此可能:
其中一个线程成功,另一个线程因NoSuchElementException而失败,因为队列中没有其他元素。
<强>更新强>
如果您只有一个生产者和一个消费者,我认为Java内存模型规范可以解释您看到的行为。
简而言之,由于对LinkedList的访问不同步,因此JVM不提供数据可见性保证。我们来看看isEmpty和removeFirst方法的实现:
来自LinkedList
transient int size = 0;
transient Node<E> first;
// ...
public int More ...size() {
return size;
}
// ...
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
来自AbstractCollection
public boolean isEmpty() {
return size() == 0;
}
如您所见,大小和元素存储在不同的变量中。因此,消费者线程可能会在“size”变量上看到更新,而在“first”上看不到更新。
答案 3 :(得分:-2)
你可以做的是使用某种技术来协调线程,比如Mutex,Semaphore,Monitor,Mailbox等。