1. 概述

Java 的BlockingQueue接口表示一个线程安全的队列。 当队列满时,尝试向队列中添加元素的线程会被阻塞;当队列为空时,尝试从中取元素的线程也会被阻塞。

BlockingQueue有多种实现,如ArrayBlockingQueueLinkedBlockingQueueSynchronousQueuePriorityBlockingQueue

在这个教程中,我们将比较ArrayBlockingQueueLinkedBlockingQueue的区别。

2. ArrayBlockingQueue

ArrayBlockingQueue是一个有界队列,内部使用数组。创建实例时可以指定数组的大小。

下面的代码片段展示了如何创建ArrayBlockingQueue实例,我们指定了内部数组的大小为10:

int INIT_CAPACITY = 10;

BlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(INIT_CAPACITY, true);

如果在队列已满时插入元素,或者初始容量小于1,add操作会抛出IllegalStateException。此外,如果我们设置的初始大小小于1,会得到IllegalArgumentException

第二个参数代表公平策略。 可选地设置公平策略可以维护阻塞生产者和消费者线程的顺序。这允许线程按照FIFO(先进先出)顺序访问队列,从而避免线程饥饿。

3. LinkedBlockingQueue

LinkedBlockingQueueBlockingQueue的一种可选有界实现,它基于链表节点。

创建实例时也可以指定容量。如果不指定,则默认容量为Integer.MAX_VALUE

当插入元素时,会动态创建链表节点。

让我们看看如何创建LinkedBlockingQueue

BlockingQueue<String> linkedBlockingQueue = new LinkedBlockingQueue<>();

4. ArrayBlockingQueueLinkedBlockingQueue比较

尽管ArrayBlockingQueueLinkedBlockingQueue都是BlockingQueue的实现,并且都以FIFO方式存储元素,但它们之间存在一些差异。现在我们来看看这些区别:

特性

ArrayBlockingQueue

LinkedBlockingQueue

实现

它基于数组

它使用链表节点

队列大小

它是有界的队列,因此创建时必须指定初始容量。

不需要指定大小。

公平策略

可以在其中设置公平策略

这个没有公平策略选项

锁数量

它使用一个ReentrantLock,put和take操作共用同一个锁。

它为读写操作使用单独的ReentrantLock,这减少了生产者和消费者线程之间的竞争。

内存空间

由于必须指定初始容量,可能会分配超过所需的内存。

通常不预分配节点,所以其内存占用与大小匹配

5. 总结

在这篇文章中,我们了解了ArrayBlockingQueueLinkedBlockingQueue之间的区别。ArrayBlockingQueue基于数组,而LinkedBlockingQueue基于链表节点。我们还讨论了ArrayBlockingQueue中存在的公平策略额外功能,以及两个队列的锁定机制和内存占用。

如往常一样,示例代码可在GitHub上找到。