LinkedBlockingQueue 的插入和删除方法是线程安全的吗?
Posted
技术标签:
【中文标题】LinkedBlockingQueue 的插入和删除方法是线程安全的吗?【英文标题】:Are LinkedBlockingQueue's insert and remove methods thread safe? 【发布时间】:2011-02-11 07:58:00 【问题描述】:我在两个不同的线程之间使用LinkedBlockingQueue
。一个线程通过add
添加数据,而另一个线程通过take
接收数据。
我的问题是,我是否需要同步访问add
和take
。 LinkedBlockingQueue
的插入和删除方法线程安全吗?
【问题讨论】:
如果它们不是线程安全的,那么您需要同步它。然后 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 更好的并发性,并且仅通过同步边缘条件来维护线程安全。插入和删除方法已智能同步,仅适用于边缘情况 这个答案不正确。add
和 take
是线程安全的,无需额外同步即可并发使用。
更正了我的答案。除队列满/空的边缘情况外,同时添加和获取不同步。
我认为答案令人困惑。我认为即使它们不同步,它们仍然是安全的,不需要手动同步。【参考方案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
随笔41 LinkedBlockingQueue,PriorityQueue,ConcurrentLinkedQueue
java并发之阻塞队列LinkedBlockingQueue与ArrayBlockingQueue