我在两个不同的线程之间使用
LinkedBlockingQueue
。一个线程通过 add
添加数据,而另一个线程通过 take
接收数据。
我的问题是,我是否需要同步访问
add
和take
。 LinkedBlockingQueue
的插入和删除方法线程安全吗?
是的。 来自文档:
“BlockingQueue 实现是 线程安全。所有排队方式 使用原子方式实现其效果 内部锁或其他形式的 并发控制。然而,大部分 集合操作addAll, containsAll、retainAll 和 removeAll 不一定执行 除非另有说明,否则以原子方式 在一个实现中。原来如此 可能,例如,对于 addAll(c) 之后失败(抛出异常) 仅添加其中的一些元素 c.”
编辑:
addAll
行为不是多线程特定的问题,因为单线程场景可能会出现完全相同的问题。
在单线程应用程序中,创建一个 ArrayList
list
,其中包含 9 个非空元素,末尾有 1 个空元素。
致电
queue.addAll(list);
假设它按顺序添加元素(我不相信这是保证的,但很可能会),它将添加前 9 个元素,然后抛出异常。 (BlockingQueue 不允许空元素。)9 仍会添加到队列中。
addAll
是原子的,无论集合是否是线程安全的,都要求很高。它需要对整个集合进行快照然后回滚。 是的,它可以为许多集合实现(尽管对于某些集合来说会很复杂),但我不认为任何集合(线程安全与否)都可以实现。底线:集合很复杂,而且它们的保证也各不相同。 查阅文档很重要。 线程安全只是许多人关心的问题之一。
是的,
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 个不同的锁定对象(队列满/空的边缘条件除外)。
简单地说,它绝对是线程安全的,否则它就没有资格作为 ThreadPoolExecutor 存储元素的候选者。
只需添加和检索元素,无需担心 BlockingQueue 的并发性。