LinkedBlockingQueue 的插入和删除方法是线程安全的吗?

Posted

技术标签:

【中文标题】LinkedBlockingQueue 的插入和删除方法是线程安全的吗?【英文标题】:Are LinkedBlockingQueue's insert and remove methods thread safe? 【发布时间】:2011-02-11 07:58:00 【问题描述】:

我在两个不同的线程之间使用LinkedBlockingQueue。一个线程通过add添加数据,而另一个线程通过take接收数据。

我的问题是,我是否需要同步访问addtakeLinkedBlockingQueue 的插入和删除方法线程安全吗?

【问题讨论】:

如果它们不是线程安全的,那么您需要同步它。然后 take() 很可能会抓取一个空队列的互斥体,阻止任何其他线程添加到它。你好死锁! 【参考方案1】:

是的,BlockingQueue 方法 add()take() 是线程安全的但有区别

add ()take() 方法使用 2 个不同的 ReentrantLock 对象。

add() 方法使用

private final ReentrantLock putLock = new ReentrantLock();

take() 方法使用

private final ReentrantLock takeLock = new ReentrantLock();

因此,同时访问add() 方法是同步的。同样,同时访问take()方法是synchronized

但是,同时访问add()take() 方法不是synchronized,因为它们使用了2 个不同的锁对象(队列满/空的边缘条件除外)。

【讨论】:

这个答案是一个有效的观察,但忽略了 LinkedBlockingQueue 的实现者已经意识到这个问题并解决了它。详情在这里:***.com/questions/26543807/… 是的,我同意。 LInkedBlockingQueue 提供比 ArrayBlockingQueue 更好的并发性,并且仅通过同步边缘条件来维护线程安全。插入和删除方法已智能同步,仅适用于边缘情况 这个答案不正确。 addtake 是线程安全的,无需额外同步即可并发使用。 更正了我的答案。除队列满/空的边缘情况外,同时添加和获取不同步。 我认为答案令人困惑。我认为即使它们不同步,它们仍然是安全的,不需要手动同步。【参考方案2】:

是的,它绝对是线程安全的,否则它就没有资格作为 ThreadPoolExecutor 存储元素的候选者。

只需添加和检索元素,无需担心 BlockingQueue 的并发性。

【讨论】:

它只是被宣传为线程安全的,实际上文档阅读“因此,例如,在 c 中仅添加一些元素后 addAll(c) 可能会失败(抛出异常) . "【参考方案3】:

是的。来自the docs:

"BlockingQueue 的实现是 线程安全的。所有排队方法 使用原子地实现它们的效果 内部锁或其他形式的 并发控制。然而,大宗 集合操作 addAll, containsAll、retainAll 和 removeAll 不一定执行 除非另有说明,否则原子地 在一个实现中。所以它是 可能的,例如,对于 addAll(c) 之后失败(抛出异常) 只添加一些元素 c.”

【讨论】:

实际上不是。不,如果您只是使用添加和获取,则不是,但如果您要使用批量操作,您将不得不同步它而不是简单的“是”?还是我读错了文档? @cproinger,不,你永远不必同步它,只要你愿意处理 addAll 在添加项目子集(或类似项目)后抛出异常。这取决于您如何定义线程安全。没错,批量方法没有原子性保证。 "因此,例如,addAll(c) 在 c 中仅添加一些元素后可能会失败(抛出异常)。"这不是“不”吗?

以上是关于LinkedBlockingQueue 的插入和删除方法是线程安全的吗?的主要内容,如果未能解决你的问题,请参考以下文章

LinkedBlockingQueue put vs offer

LinkedBlockingQueue

随笔41 LinkedBlockingQueue,PriorityQueue,ConcurrentLinkedQueue

java并发之阻塞队列LinkedBlockingQueue与ArrayBlockingQueue

Java数据结构及算法实战系列010:Java队列04——链表实现的阻塞队列LinkedBlockingQueue

Java数据结构及算法实战系列010:Java队列04——链表实现的阻塞队列LinkedBlockingQueue