"BlockingQueue 实现是线程安全的。所有排队方法都使用内部锁定或其他形式的并发控制以原子方式实现其效果。但是,除非在实现中另有规定,否则不保证批量集合操作 addAll、containsAll、retainAll 和 removeAll 是原子执行的。因此,例如,在仅添加 c 中的一些元素后,addAll(c) 可能会失败(抛出异常)。
由于在 LinkedBlockingQueue.addAll() 操作的描述中没有特别说明,因此我必须假设它不是线程安全的。
你是否同意我的观点,为了保证通过 addAll() 添加的所有元素是连续的(即一起添加),唯一的解决方案是每次修改队列(使用 add 或 take 操作)时都使用 Lock?例如:
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()
是一个阻塞操作,并且由于需要锁来向队列添加元素,一旦队列为空,程序将进入死锁状态:写入者无法写入,因为锁被take()
占用,同时take()
将处于阻塞状态,直到队列中有内容被写入(但由于上述原因,这是不可能的)。有什么想法吗?我认为最明显的想法是在take()
周围去掉锁定,但这样可能无法保证addAll()
的所需原子性。
BlockingQueue
的访问都通过那个Lock
进行时,这才是一个解决方案。 - Sotirios Delimanolis