BlockingQueue的drainTo()方法的线程安全性

Sha*_*ore 14 java thread-safety blockingqueue

BlockingQueue的文档说批量操作不是线程安全的,尽管它没有明确提到方法drainTo().

BlockingQueue实现是线程安全的.所有排队方法都使用内部锁或其他形式的并发控制以原子方式实现其效果.但是,除非在实现中另有说明,否则批量收集操作addAll,containsAll,retainAll和removeAll不一定以原子方式执行.因此,例如,在仅添加c中的一些元素之后,addAll(c)可能会失败(抛出异常).

drainTo()方法的文档指定无法以线程安全的方式修改阻塞BlockingQueue元素的集合.但是,它没有提到有关drainTo()操作是线程安全的任何事情.

从此队列中删除所有可用元素,并将它们添加到给定集合中.此操作可能比重复轮询此队列更有效.尝试向集合c添加元素时遇到的故障可能导致在抛出关联的异常时元素既不在集合中,也不在集合中.尝试将队列排放到自身会导致IllegalArgumentException.此外,如果在操作正在进行时修改指定的集合,则此操作的行为是不确定的.

那么,drainTo()方法是线程安全的吗?换句话说,如果一个线程在阻塞队列上调用了drainTo()方法,而另一个线程在同一个队列上调用了add()或put(),那么在两个操作结束时队列的状态是否一致?

Ste*_*n C 16

我认为你混淆了"线程安全"和"原子"这两个术语.它们并不代表同一件事.方法可以是线程安全的而不是原子的,并且可以是原子的(对于单个线程)而不是线程安全的.

线程安全是一个橡胶术语,很难定义而不是循环.根据Goetz的说法,一个好的工作模型是,如果一个方法在多线程上下文中使用它是"正确的",因为它在单线程上下文中运行,它是线程安全的.除非你有一个正式的规范来衡量,否则正确性是主观的.

相比之下,原子很容易定义.它只是意味着操作要么完全发生,要么根本不发生.

所以你的问题的答案drainTo()是线程安全的,但不是原子的.它不是原子的,因为它可以通过排水中途抛出异常.但是,模块化的是,队列仍将处于一致状态,无论其他线程是否同时对队列执行操作.


(在上面的讨论中隐含的是,BlockingQueue接口的具体实现正确地实现了接口.如果没有,所有的赌注都会关闭.)


Aar*_*lla 5

drainTo()是某种意义上的线程安全,即同时发生的队列上的任何操作都不会改变结果,也不会破坏队列的状态.否则,该方法将毫无意义.

如果目标集合(添加结果的集合)做了"聪明"的事情,则可能会遇到问题.但是,由于您通常将队列排放到只有一个线程可以访问的集合,因此更多的是理论问题.