Java BlockingQueue-阻塞的drainTo()方法

Java BlockingQueue-阻塞的drainTo()方法,java,concurrency,Java,Concurrency,BlockingQueue有一个名为drainTo()的方法,但它没有被阻止。我需要一个队列,我想阻止,但也能够检索排队的对象在一个单一的方法 Object first = blockingQueue.take(); if ( blockingQueue.size() > 0 ) blockingQueue.drainTo( list ); 我想上面的代码可以工作,但我正在寻找一个优雅的解决方案。您指的是: 此外,如果指定的集合 在操作进行过程中修改 我相信这是指您示例中的集合

BlockingQueue有一个名为drainTo()的方法,但它没有被阻止。我需要一个队列,我想阻止,但也能够检索排队的对象在一个单一的方法

Object first = blockingQueue.take();

if ( blockingQueue.size() > 0 )
    blockingQueue.drainTo( list );

我想上面的代码可以工作,但我正在寻找一个优雅的解决方案。

您指的是:

此外,如果指定的集合 在操作进行过程中修改

我相信这是指您示例中的集合
列表

blockingQueue.drainTo(list);
这意味着您不能在从
阻塞队列
排入
列表
的同时修改
列表
。但是,阻塞队列在内部进行同步,以便在调用
drainTo
时,put和(参见下面的注释)get将阻塞。如果它不这样做,那么它就不会是真正的线程安全的。您可以查看源代码并验证
drainTo
对于阻塞队列本身是线程安全的

或者,您的意思是,当您调用
drainTo
时,您希望它阻止,直到至少有一个对象添加到队列中?在这种情况下,您除了以下选择之外别无选择:

list.add(blockingQueue.take());
blockingQueue.drainTo(list);
阻止,直到添加了一个或多个项目,然后将整个队列排入集合
列表


注意:从Java7开始,一个单独的锁用于get和put。现在允许在drainTo期间执行Put操作(以及许多其他take操作)。

有了可用的API,我认为您不会变得更加优雅。除此之外,您可以删除大小测试

如果您希望在另一个删除操作同时进行的情况下自动检索连续的元素序列,我认为即使是
drainTo
也不能保证这一点。

源代码:

 596:     public int drainTo(Collection<? super E> c) {
              //arg. check
 603:         lock.lock();
 604:         try {
 608:             for (n = 0 ; n != count ; n++) {
 609:                 c.add(items[n]);
 613:             }
 614:             if (n > 0) {
 618:                 notFull.signalAll();
 619:             }
 620:             return n;
 621:         } finally {
 622:             lock.unlock();
 623:         }
 624:     }

596:public int drainTo(Collection我发现这个模式很有用

List<byte[]> blobs = new ArrayList<byte[]>();
if (queue.drainTo(blobs, batch) == 0) {
   blobs.add(queue.take());
}
List blob=new ArrayList();
if(queue.drainTo(blob,batch)==0){
add(queue.take());
}

如果你碰巧使用谷歌番石榴,有一个很好的方法

将队列作为BlockingQueue.drainTo(Collection,int)
排出,但如果 请求的
numElements
元素不可用,它将等待 在指定的超时时间之前删除它们


drainTo肯定会阻止任何put、get、take等。请检查源代码。此API对阻止队列的其他调用是线程安全的。但是,如果在drainTo过程中要向其排放的集合发生更改,则可能会出现问题。好的,没错,很有趣,但除此之外。请检查JDK中的实现类。(这显然是我的意思。)它们不是唯一可能的实现类。例如,我确信将ConcurrentLinkedQueue转换为“ConcurrentLinkedBlockingQueue”相对简单(!)(尽管显然阻塞操作不会是无等待的)。您要复制到的集合不太可能是线程安全的。重点是什么?是否保证drainTo(list)不会调用list.clear()?我在javadoc的任何地方都看不到这一点。@Recurse:javadoc不保证drainTo(list)不会调用clear()我想,因为它并没有说它不会这样做。然而,如果它做了一个对其没有记录的行为的一个集合,那么它将是非常令人惊讶的。许多人会认为这是一个严重的错误。“所有可用要素"建议使用非阻塞。我已经为数组和列表阻塞队列的Android SDK实现确认了这一点。@bacar:虽然corsiKa说这是Java 7的一个变化,但我不认为JavaDoc中的哪一个地方保证获取和放置使用单独的锁。可能是JVM实现类碰巧都这样做了,但我不知道是什么原因这是可以保证的。无论如何,
drainTo
本身并不是一个阻塞操作。它返回当前队列中发生的内容。阻塞队列确实提供了您所说的阻塞操作,但
drainTo
不是其中之一。因此,在
drainTo
期间显式阻塞put没有意义,除非你需要这样做才能保证一致性。不。使用锁不仅可以提供互斥,还可以确保看到的计数是“新鲜的”,即可以看到其他线程对主内存的更新。你可以进行“计数”易失性,但易失性写操作相对较慢,因此写操作将意味着同时持有锁和易失性写操作,这可能会增加总开销。任何编写并发代码的开发人员都应该尽力理解java内存模型: