一、 BlockingQueue简介
1、 BlockingQueue
ArrayBlockingQueue
、LinkedBlockingQueue
、PriorityBlockingQueue
、ConcurrentLinkedQueue
和 DelayQueue
是 Java 中 java.util.concurrent
包提供的几种队列实现。它们都支持多线程环境下的安全操作,但在实现方式和特性上有所不同。
2、共同点
- 线程安全:所有这些队列都是线程安全的,可以在多线程环境中安全使用。
- 接口实现:它们都实现了
java.util.Queue
接口,并提供了类似的队列操作方法,如offer()
,poll()
,peek()
等。
3、不同点
1. ArrayBlockingQueue
- 实现方式:基于数组的有界阻塞队列。
- 容量:在创建时必须指定固定的容量,满时添加元素将阻塞,空时移除元素将阻塞。
- 有界性:是有界队列,容量固定。
- 公平性:可选的公平性策略,使用公平策略时,线程按 FIFO 顺序访问队列。
2. LinkedBlockingQueue
- 实现方式:基于链表的可选有界阻塞队列。
- 容量:可以指定容量限制,也可以不指定(不指定时,相当于无界)。
- 有界性:可有界也可无界。
- 性能:在高并发情况下性能较好,因为它对生产者和消费者使用了独立的锁。
3. PriorityBlockingQueue
- 实现方式:基于堆结构的无界阻塞优先级队列。
- 排序:元素根据自然顺序或提供的比较器进行排序,优先级高的元素会先被处理。
- 有界性:无界。
- 注意:没有使用公平性策略,也不保证元素的 FIFO 顺序,只保证优先级。
4. ConcurrentLinkedQueue
- 实现方式:基于链表的无界非阻塞队列。
- 有界性:无界。
- 无阻塞性:不使用锁,通过 CAS 操作来实现线程安全。
- 应用场景:适合在高并发情况下的队列操作,特别是在对性能要求较高但对操作顺序无严格要求的情况下。
5. DelayQueue
- 实现方式:基于优先级队列的无界阻塞队列,内部元素必须实现
Delayed
接口。 - 特点:只有当元素的延迟时间到期后,才能从队列中获取元素。
- 有界性:无界。
- 用途:常用于定时任务调度,例如延时执行任务或限时缓存清理。
4、总结
有界与无界:
ArrayBlockingQueue
是有界的,必须指定容量。LinkedBlockingQueue
可有界也可无界。PriorityBlockingQueue
、ConcurrentLinkedQueue
和DelayQueue
是无界的。
阻塞与非阻塞:
ArrayBlockingQueue
、LinkedBlockingQueue
和PriorityBlockingQueue
是阻塞队列,支持阻塞的操作。ConcurrentLinkedQueue
是非阻塞队列,使用 CAS 实现线程安全,不会阻塞线程。DelayQueue
是阻塞队列,但只有当元素的延迟到期时才能获取元素。
使用场景:
ArrayBlockingQueue
适用于有容量限制的场景。LinkedBlockingQueue
适用于容量可能会变化或者不需要限制的场景。PriorityBlockingQueue
适用于需要按优先级处理元素的场景。ConcurrentLinkedQueue
适用于高并发、无界的场景。DelayQueue
适用于需要延迟执行或定时调度任务的场景。
这些队列在不同的应用场景中提供了灵活性和并发性能,选择哪种队列取决于具体需求,如是否需要有界、是否需要阻塞操作、是否需要按优先级排序等。
二、通用方法介绍
下面分别列出 ArrayBlockingQueue
、LinkedBlockingQueue
、PriorityBlockingQueue
、ConcurrentLinkedQueue
和 DelayQueue
的常用操作方法。所有这些队列都实现了 java.util.Queue
接口,因此一些通用的方法在这些类中都有定义。
ArrayBlockingQueue
添加和移除元素:
boolean add(E e)
:在队列尾部插入指定元素,如果成功返回true
,如果队列已满则抛出IllegalStateException
。boolean offer(E e)
:在队列尾部插入指定元素,如果成功返回true
,否则返回false
。E remove()
:移除并返回队列头部的元素,如果队列为空则抛出NoSuchElementException
。E poll()
:移除并返回队列头部的元素,如果队列为空则返回null
。E take()
:移除并返回队列头部的元素,若队列为空则等待直到有元素可用。boolean remove(Object o)
:从队列中移除指定元素(与Queue
接口中定义的一致)。
查看元素:
E element()
:返回队列头部的元素,但不移除。如果队列为空则抛出NoSuchElementException
。E peek()
:返回队列头部的元素,但不移除。如果队列为空则返回null
。
其他方法:
int size()
:返回队列中的元素数量。int remainingCapacity()
:返回队列剩余的容量。boolean contains(Object o)
:检查队列中是否包含指定的元素。void clear()
:移除队列中的所有元素。
LinkedBlockingQueue
添加和移除元素:
boolean add(E e)
:在队列尾部插入指定元素,如果队列已满则抛出IllegalStateException
。boolean offer(E e)
:在队列尾部插入指定元素,如果成功返回true
,否则返回false
。E remove()
:移除并返回队列头部的元素,如果队列为空则抛出NoSuchElementException
。E poll()
:移除并返回队列头部的元素,如果队列为空则返回null
。E take()
:移除并返回队列头部的元素,若队列为空则等待直到有元素可用。void put(E e)
:在队列尾部插入指定元素,如果队列满则等待。
查看元素:
E element()
:返回队列头部的元素,但不移除。如果队列为空则抛出NoSuchElementException
。E peek()
:返回队列头部的元素,但不移除。如果队列为空则返回null
。
其他方法:
int size()
:返回队列中的元素数量。int remainingCapacity()
:返回队列的剩余容量。boolean contains(Object o)
:检查队列中是否包含指定的元素。void clear()
:移除队列中的所有元素。
PriorityBlockingQueue
添加和移除元素:
boolean add(E e)
:在队列中插入指定元素,队列根据元素的优先级进行排序。boolean offer(E e)
:在队列中插入指定元素,队列根据元素的优先级进行排序。E remove()
:移除并返回队列中具有最高优先级的元素,如果队列为空则抛出NoSuchElementException
。E poll()
:移除并返回队列中具有最高优先级的元素,如果队列为空则返回null
。E take()
:移除并返回队列中具有最高优先级的元素,如果队列为空则等待直到有元素可用。
查看元素:
E element()
:返回队列中具有最高优先级的元素,但不移除。如果队列为空则抛出NoSuchElementException
。E peek()
:返回队列中具有最高优先级的元素,但不移除。如果队列为空则返回null
。
其他方法:
int size()
:返回队列中的元素数量。boolean contains(Object o)
:检查队列中是否包含指定的元素。void clear()
:移除队列中的所有元素。
ConcurrentLinkedQueue
添加和移除元素:
boolean add(E e)
:在队列尾部插入指定元素,如果成功返回true
。boolean offer(E e)
:在队列尾部插入指定元素,如果成功返回true
。E remove()
:移除并返回队列头部的元素,如果队列为空则抛出NoSuchElementException
。E poll()
:移除并返回队列头部的元素,如果队列为空则返回null
。
查看元素:
E element()
:返回队列头部的元素,但不移除。如果队列为空则抛出NoSuchElementException
。E peek()
:返回队列头部的元素,但不移除。如果队列为空则返回null
。
其他方法:
int size()
:返回队列中的元素数量。boolean contains(Object o)
:检查队列中是否包含指定的元素。void clear()
:移除队列中的所有元素。
DelayQueue
添加和移除元素:
boolean add(E e)
:在队列中插入指定元素,如果元素没有延迟则抛出IllegalArgumentException
。boolean offer(E e)
:在队列中插入指定元素,如果元素没有延迟则抛出IllegalArgumentException
。void put(E e)
:在队列中插入指定元素,如果元素没有延迟则抛出IllegalArgumentException
。E remove()
:移除并返回队列头部的元素,如果队列为空则抛出NoSuchElementException
。E poll()
:移除并返回队列头部的元素,如果没有元素到期则返回null
。E take()
:移除并返回队列头部的元素,如果没有元素到期则等待。
查看元素:
E peek()
:返回队列头部的元素,但不移除。如果没有元素到期则返回null
。
其他方法:
int size()
:返回队列中的元素数量。boolean contains(Object o)
:检查队列中是否包含指定的元素。void clear()
:移除队列中的所有元素。
总结
每个队列的具体方法可能略有不同,尤其是在处理阻塞、非阻塞、优先级和延迟方面时。选择哪种队列取决于应用程序的具体需求,如是否需要有序处理、是否需要延迟处理等。
3、成员变量
下面列出 ArrayBlockingQueue
、LinkedBlockingQueue
、PriorityBlockingQueue
、ConcurrentLinkedQueue
和 DelayQueue
的关键成员变量。这些成员变量定义了队列的内部状态和行为。
ArrayBlockingQueue
ArrayBlockingQueue
是一个基于数组的有界阻塞队列,以下是其主要成员变量:
items
- 类型:
Object[]
- 描述:用于存储队列元素的数组。
ArrayBlockingQueue
是基于数组的实现。
- 类型:
count
- 类型:
int
- 描述:队列中当前元素的数量。
- 类型:
takeIndex
- 类型:
int
- 描述:表示下一个将要被取出元素的索引。
- 类型:
putIndex
- 类型:
int
- 描述:表示下一个元素插入的位置索引。
- 类型:
lock
- 类型:
ReentrantLock
- 描述:用于控制对队列的并发访问,保证线程安全。
- 类型:
notEmpty
- 类型:
Condition
- 描述:当队列非空时,用于通知等待获取元素的线程。
- 类型:
notFull
- 类型:
Condition
- 描述:当队列未满时,用于通知等待插入元素的线程。
- 类型:
fair
- 类型:
boolean
- 描述:指示是否使用公平锁机制。
- 类型:
LinkedBlockingQueue
LinkedBlockingQueue
是一个基于链表的阻塞队列,可以有界也可以无界,以下是其主要成员变量:
head
- 类型:
Node<E>
- 描述:链表的头结点,不存储数据,指向第一个实际元素的前一个节点。
- 类型:
last
- 类型:
Node<E>
- 描述:指向链表的尾结点,新的元素将插入在尾结点之后。
- 类型:
count
- 类型:
AtomicInteger
- 描述:队列中当前元素的数量。
- 类型:
capacity
- 类型:
int
- 描述:队列的容量限制,如果未指定则为最大值
Integer.MAX_VALUE
。
- 类型:
takeLock
- 类型:
ReentrantLock
- 描述:用于控制对队列的取出操作的并发访问。
- 类型:
putLock
- 类型:
ReentrantLock
- 描述:用于控制对队列的插入操作的并发访问。
- 类型:
notEmpty
- 类型:
Condition
- 描述:当队列非空时,用于通知等待获取元素的线程。
- 类型:
notFull
- 类型:
Condition
- 描述:当队列未满时,用于通知等待插入元素的线程。
- 类型:
PriorityBlockingQueue
PriorityBlockingQueue
是一个基于堆的无界优先级队列,以下是其主要成员变量:
queue
- 类型:
Object[]
- 描述:存储队列元素的数组,堆结构实现的基础。
- 类型:
size
- 类型:
int
- 描述:当前队列中的元素数量。
- 类型:
comparator
- 类型:
Comparator<? super E>
- 描述:用于元素比较的比较器,决定元素的优先级。
- 类型:
lock
- 类型:
ReentrantLock
- 描述:用于控制对队列的并发访问,保证线程安全。
- 类型:
notEmpty
- 类型:
Condition
- 描述:当队列非空时,用于通知等待获取元素的线程。
- 类型:
ConcurrentLinkedQueue
ConcurrentLinkedQueue
是一个基于链表的无界非阻塞队列,以下是其主要成员变量:
head
- 类型:
AtomicReference<Node<E>>
- 描述:指向队列头部的指针,
Node<E>
是链表的节点类。
- 类型:
tail
- 类型:
AtomicReference<Node<E>>
- 描述:指向队列尾部的指针,表示下一个元素应插入的位置。
- 类型:
DelayQueue
DelayQueue
是一个基于堆的无界阻塞队列,专门处理带有延迟时间的元素,以下是其主要成员变量:
queue
- 类型:
PriorityQueue<E>
- 描述:内部使用优先级队列存储元素,按照元素的到期时间排序。
- 类型:
leader
- 类型:
Thread
- 描述:当前正在等待最早到期元素的线程。
- 类型:
lock
- 类型:
ReentrantLock
- 描述:用于控制对队列的并发访问,保证线程安全。
- 类型:
available
- 类型:
Condition
- 描述:用于通知线程队列中有到期的元素可以被取出。
- 类型:
总结
这些成员变量是每个队列的核心组成部分,决定了它们的内部数据结构、并发控制机制和操作行为。了解这些成员变量及其作用有助于深入理解这些队列的实现原理和使用场景。
4、其他特性
在介绍的几种 Java 并发队列中,有些队列有其独特的特性或使用场景。以下是对一些关键特性和场景的特殊解释:
ArrayBlockingQueue
- 有界队列:
ArrayBlockingQueue
是一个有界的阻塞队列,这意味着它有一个固定的容量。当队列满时,尝试向队列添加新元素的操作将被阻塞,直到有空间可用。类似地,当队列为空时,尝试从队列获取元素的操作也会被阻塞,直到有新元素可用。 - 公平性:可以选择启用公平性策略,当启用时,队列会以 FIFO 顺序处理线程请求,从而避免线程饥饿。
LinkedBlockingQueue
- 链表结构:
LinkedBlockingQueue
基于链表实现,可以是有界也可以是无界的。即使在容量设置上,它也有可能在实际使用中表现为无界(例如容量非常大时)。 - 独立锁机制:它使用独立的锁来控制生产者和消费者,因此在高并发情况下性能较好,因为生产者和消费者可以在不同的锁上操作,减少了锁竞争。
PriorityBlockingQueue
- 优先级排序:
PriorityBlockingQueue
是一个无界队列,它的特殊之处在于内部维护了元素的优先级顺序。元素按优先级顺序进行排列,优先级最高的元素会先被处理。它使用自然顺序或自定义的比较器来确定优先级。 - 无公平性保证:尽管有序,它并不保证对元素的访问是公平的,这意味着某些元素可能会因为优先级较低而长期滞留在队列中。
ConcurrentLinkedQueue
- 无阻塞性:
ConcurrentLinkedQueue
是无阻塞的,这意味着它不使用传统的锁机制,而是利用 CAS(Compare-And-Swap)操作来实现线程安全。这个特性使得它在高并发情况下性能非常好,因为线程不会因为锁竞争而阻塞。 - 无界:它是无界队列,没有容量限制,适合需要快速、非阻塞访问的场景。
DelayQueue
- 延迟处理:
DelayQueue
是一个无界的优先级队列,但其特殊之处在于元素必须实现Delayed
接口。每个元素都有一个到期时间,只有到期的元素才能被从队列中取出。在某些情况下,元素可能永远不会到期,因此需要仔细管理元素的生命周期。 - 常见应用:常用于需要延迟执行任务的场景,如定时任务调度、缓存过期等。
总结与使用场景
ArrayBlockingQueue
和LinkedBlockingQueue
:适用于生产者-消费者模式,特别是在需要有界控制(如限制资源使用)的情况下。PriorityBlockingQueue
:适用于需要根据优先级处理任务的场景,如任务调度系统。ConcurrentLinkedQueue
:适合在需要快速无锁操作的高并发场景,如日志记录系统、事件处理系统等。DelayQueue
:用于需要延迟执行的场景,如定时任务、限时缓存等。
每种队列都有其独特的设计和用途,理解其特性有助于在合适的场景中选择合适的队列,以实现最佳的性能和正确性。