gpt4 book ai didi

java - LinkedBlockingQueue 中的 addAll() 是线程安全的(如果不是,则为解决方案)?

转载 作者:塔克拉玛干 更新时间:2023-11-02 19:21:09 33 4
gpt4 key购买 nike

引用文档:

"BlockingQueue implementations are thread-safe. All queuing methods achieve their effects atomically using internal locks or other forms of concurrency control. However, the bulk Collection operations addAll, containsAll, retainAll and removeAll are not necessarily performed atomically unless specified otherwise in an implementation. So it is possible, for example, for addAll(c) to fail (throwing an exception) after adding only some of the elements in c."

由于在 LinkedBlockingQueue.addAll() 操作的描述中没有写任何特别的东西,我不得不假设这不是线程安全的。

你是否同意我的观点,为了保证通过 addAll() 添加的所有元素都是连续的(即添加在一起)的唯一解决方案是每次队列是时使用 Lock修改(使用 addtake 操作)?例如:

BlockingQueue<T> queue = new LinkedBlockingQueue<>();
Lock lock = new ReentrantLock();

//somewhere, some thread...
lock.lock();
queue.addAll(someCollection);
lock.unlock();

//somewhere else, (maybe) some other thread...
lock.lock();
queue.take();
lock.unlock();

重要更新:哇,在前面的例子中没有人看到一个大错误:因为 take() 是一个阻塞操作,并且因为需要锁才能将元素添加到队列中,一旦队列将被empty 程序会进入死锁状态:writer不能写,因为锁被take()占有,同时take()会被处于阻塞状态,直到将某些内容写入队列(由于先前的原因,这不会发生)。 有什么想法吗?我认为最明显的方法是移除 take() 周围的锁,然后可能需要 addAll() 的原子性不能保证。

最佳答案

addAll 仍然是线程安全的,只是不提供原子性。所以这取决于您的用例/期望。

如果您在没有显式锁定的情况下使用 addAll,那么如果其他线程尝试写入队列(添加新元素),则无法保证添加元素的顺序,并且它们可能会混合。如果这是一个问题而不是你需要锁定。但是 addAll 仍然是线程安全的,不会有队列的损坏。

但通常情况下,队列用于提供许多读取器/写入器之间的通信方式,并且可能不需要严格保留插入顺序。

现在,主要问题是,如果队列已满,add 方法会抛出异常,因此 addAll 操作可能会在中间崩溃,您不知道哪些元素被添加了哪些没有。

如果您的用例允许等待空间插入元素,那么您应该使用循环中的 put。

for (E e: someCollection) queue.put(e);

这将阻塞直到有空间添加其他元素。

手动加锁比较棘手,因为访问队列时总是要记得加锁,容易出错。因此,如果您确实需要原子性,请编写一个实现 BlockingQUeue 接口(interface)但在调用底层操作之前使用锁定的包装类。

关于java - LinkedBlockingQueue 中的 addAll() 是线程安全的(如果不是,则为解决方案)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28968490/

33 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com