线程安全问题

时间:2016-10-11 11:42:24

标签: java multithreading thread-safety

我有一个带有对象的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)

我不知道,为什么我会得到这个例外,因为它只应该尝试删除一个对象(如果存在)。

4 个答案:

答案 0 :(得分:2)

正如尼古拉斯已经提到的,你需要一个线程安全的实现。我建议使用LinkedBlockingQueue

您可以使用offer方法添加,并使用take删除,这也将解决您的“忙碌等待”问题。

答案 1 :(得分:0)

LinkedList 不是线程安全的,所以你不能像现在这样分享几个线程,否则你将面临像这样的不可预测的错误,因为并发修改导致在不一致的状态下,请使用线程安全的双端,例如ConcurrentLinkedDeque

答案 2 :(得分:0)

虽然我认为已经提供了一些关于如何解决问题的好解决方案,但没有一个答案解释了为什么@BluE会看到NoSuchElementException。所以这就是我认为可能发生的事情。

由于LinkedList访问未同步,因此可能:

  1. 生产者线程向队列添加元素
  2. 两个消费者线程同时检查if(!queue.isEmpty())并查看它是不是。
  3. 两个使用者线程都继续尝试从队列中调用MyObject first = queue.removeFirst();
  4. 其中一个线程成功,另一个线程因NoSuchElementException而失败,因为队列中没有其他元素。

    <强>更新

  5. 如果您只有一个生产者和一个消费者,我认为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等。