是否同步到足以使 BlockingQueue 的 drainTo() 方法原子化?

Is synchronized enough to make the drainTo() method of a BlockingQueue atomic?

如果我只是这样做:

synchronized(taskQueue) { //taskQueue is a BlockingQueue 
  taskQueue.drainTo(tasks); //tasks is a list
}

我确定不能在同步块内执行对 taskQueue.put()taskQueue.take() 的并发调用吗?

换句话说,我是否使 drainTo() 方法成为原子方法?

或者更一般地说,如何使线程安全操作的组合成为原子?

示例:

if(taskQueue.size() == 1) {
   /*Do a lot of things here, but I do not want other threads
     to change the size of the queue here with take or put*/
}
//taskQueue.size() must still be equal to 1

以LinkedBlockingQueue为例,它的成员变量有'takeLock'和'putLock'

所以客户端同步在这里没有帮助,因为其他 'take' 操作不受此锁保护,即使此锁来自队列本身。

drainTo() 方法由 'takeLock' 保护,对于任何其他 'take' 操作,它是线程安全的。但是对于'put'的操作,它们被'putLock'守护着,所以不会受到影响。

所以我认为这里不需要什么!

请参阅以下摘自 Java docs of BlockingQueue

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.

此外,请检查显示 BlockingQueue 实现可以安全地与多个生产者和多个消费者一起使用的示例。

因此,如果您不使用像 addAll, containsAll, retainAll and removeAll 这样的批量收集操作,那么您是线程安全的。

你甚至不需要 synchronized(taskQueue) { 并且可以直接使用 taskQueue.drainTo(tasks); 因为 BlockingQueue 实现是线程安全的 对于像这样的非批量收集操作puttakedrainTo

希望对您有所帮助!