在哪些情况下应该使用BlockingQueue实现而不是简单队列实现?

30

我认为应该重新表述我的问题:

在何种情况下应该使用 BlockingQueue 实现而不是简单队列实现?

变为:

相对于 Queue 的实现,考虑到速度、并发性或其他属性(如访问最后一个元素所需的时间),BlockingQueue 有什么优缺点?

我曾经使用过这两种类型的队列。我知道 BlockingQueue 通常用于并发应用程序中。我正在编写一个简单的 ByteBuffer 池,需要一些占位符来存储 ByteBuffer 对象。我需要最快的、线程安全的队列实现。即使有像 ArrayList 这样具有恒定访问时间的列表实现也是如此。

有人能讨论一下 BlockingQueue 和 Queue 以及 List 实现的优缺点吗?

目前,我已经使用 ArrayList 来保存这些 ByteBuffer 对象。

应该使用哪种数据结构来保存这些对象?

3个回答

40
一个容量有限的BlockingQueue也可以帮助你控制某种类型请求的速度。使用无界队列,生产者可能会远远领先于消费者。任务最终会被执行(除非它们导致OutOfMemoryError),但是生产者可能已经放弃很久了,所以努力是浪费的。
在这种情况下,更好的方法是向潜在的生产者发出队列已满的信号,并快速失败。例如,生产者可能是一个Web请求,用户不想等待太长时间,即使在等待时它不会消耗太多CPU周期,但它正在使用像套接字和一些内存这样的有限资源。放弃将使已排队的任务更有机会及时完成。

关于修改后的问题,我的理解是:“在池中存储对象的好的集合是什么?”

对于许多池而言,一个无边界的LinkedBlockingQueue是一个不错的选择。然而,根据您的池管理策略,ConcurrentLinkedQueue也可能适用。

在池应用程序中,阻塞“put”是不合适的。控制队列的最大大小是池管理器的工作——它决定何时为池创建或销毁资源。池的客户端从池中借用和返回资源。添加新对象或将以前借用的对象返回到池中应该是快速、非阻塞的操作。因此,有界容量队列不适用于池。

另一方面,当从池中检索对象时,大多数应用程序希望等待资源可用。阻塞式的“取”操作至少是暂时有效的,比“忙等待”——重复轮询直到资源可用——更加高效。在这种情况下,LinkedBlockingQueue 是一个不错的选择。借用者可以无限期地使用 take 阻塞,或使用 poll 限制它愿意阻塞的时间。
较少见的情况是客户端根本不愿阻塞,但如果池为空,则有能力为自己创建资源。在这种情况下,ConcurrentLinkedQueue 是一个不错的选择。这是一种灰色地带,希望尽可能共享资源(例如内存),但速度更加重要。在最坏的情况下,这会退化为每个线程都有自己的资源实例;那么与其费心试图在线程之间共享,不如不共享更有效率。
这些集合在并发应用程序中提供了良好的性能和易用性。对于非并发应用程序,ArrayList 很难被击败。即使对于动态增长的集合,LinkedList 的每个元素开销也允许带有一些空槽的 ArrayList 在内存方面保持竞争力。

1
感谢 Erickson 的精彩解释。它解决了我的问题。 - Vaibhav Kamble

4
你会在多线程场景中看到 BlockingQueue。例如,如果你想使用构造器创建 ThreadPoolExecutor,你需要将一个 BlockingQueue 作为参数传递进去。根据你传递的队列类型,执行器的行为可能会有所不同。

4
这是一个实现了队列(Queue)的功能,除此之外还支持以下操作:
- 在获取元素时等待队列变为非空 - 在存储元素时等待队列中有足够的空间
如果你需要以上功能,则可以使用阻塞队列(Blocking Queue)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接