并发集合-BlockingQueue系列
BlockingQueue
1. BlockingQueue 概述
BlockingQueue 是 Java 并发包 (java.util.concurrent) 中的一个接口,代表一个线程安全的阻塞队列。
它扩展了 Queue 接口,并提供了阻塞插入和移除操作:
- 当队列满时,插入操作会被阻塞,直到队列有空闲空间。
- 当队列空时,移除操作会被阻塞,直到队列有可用元素。
核心方法
| 方法 | 说明 |
|---|---|
put(E e) |
阻塞插入,队列满时等待 |
take() |
阻塞移除,队列空时等待 |
offer(E e, long timeout, TimeUnit unit) |
超时插入,队列满时等待一段时间 |
poll(long timeout, TimeUnit unit) |
超时移除,队列空时等待一段时间 |
add(E e) |
非阻塞插入,队列满时抛异常 |
remove() |
非阻塞移除,队列空时抛异常 |
offer(E e) |
非阻塞插入,队列满时返回 false |
poll() |
非阻塞移除,队列空时返回 null |
2. BlockingQueue 主要实现类
Java 提供了多种 BlockingQueue 实现,适用于不同场景:
(1) ArrayBlockingQueue
源码分析
public class ArrayBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable {
final Object[] items; // 存放元素的数组
int takeIndex; // 下一次出队元素的索引
int putIndex; // 下一次入队元素的索引
int count; // 元素个数
final ReentrantLock lock; // 锁
private final Condition notEmpty; // 锁的条件变量
private final Condition notFull; // 锁的条件变量
// 构造方法(带容量)
public ArrayBlockingQueue(int capacity) {
this(capacity, false); // 默认非公平锁
}
// 构造方法(带容量的同时指定是否公平)
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0) throw new IllegalArgumentException();
this.items = new Object[capacity];
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
// 构造方法
public ArrayBlockingQueue(int capacity, boolean fair, Collection<? extends E> c) { }
// 内部添加元素的方法(私有)
private void enqueue(E x) {
final Object[] items = this.items;
items[putIndex] = x; // 保存元素
if (++putIndex == items.length) // 1,每次元素入队 putIndex +1;2,当队列满了 putIndex 置为 0
putIndex = 0; // 置为0的原因:形成环形数组,使得数组可以循环利用
count++; // 元素个数+1
notEmpty.signal(); // 唤醒一个等待 notEmpty 条件的一个线程(条件队列,唤醒消费者)
}
// 内部移除元素的方法(私有)
private E dequeue() {
final Object[] items = this.items;
@SuppressWarnings("unchecked")
E x = (E) items[takeIndex]; // 先得到要移除的元素
items[takeIndex] = null; // 数组位置元素置为空
if (++takeIndex == items.length) // 同 putIndex,也要每次移除累加,也要形成环形数组
takeIndex = 0;
count--; // 元素个数-1
if (itrs != null) // 维护迭代器状态(如果有活跃的迭代器)
itrs.elementDequeued(); // 通知迭代器元素已被移除
notFull.signal(); // 唤醒一个等待 notFull 条件的一个线程(条件队列,唤醒生产者)
return x; // 返回移除的元素
}
// 阻塞入队
public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length) // count 是元素个数,items 是存储元素的数组。当队列满了就 await() 阻塞
notFull.await();
enqueue(e); // 队列未满就添加
} finally {
lock.unlock();
}
}
// 阻塞出队
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0) // 当数组为空时阻塞住
notEmpty.await();
return dequeue(); // 队列不为空就移除元素并返回移除的元素
} finally {
lock.unlock();
}
}
}
特性总结
- 基于数组实现的有界阻塞队列(初始化时必须指定容量)。
- FIFO(先进先出) 顺序。
- 默认非公平锁(可配置为公平锁)。
- 固定大小,不能扩容
(2) LinkedBlockingQueue
源码分析
public class LinkedBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable {
private final int capacity; // 队列大小
private final AtomicInteger count = new AtomicInteger(); // 元素个数
transient Node<E> head; // 头结点
private transient Node<E> last; // 尾节点
private final ReentrantLock takeLock = new ReentrantLock(); // 出队锁
private final Condition notEmpty = takeLock.newCondition(); // 出队锁条件变量
private final ReentrantLock putLock = new ReentrantLock(); // 入队锁
private final Condition notFull = putLock.newCondition(); // 入队锁条件变量
// 节点内部类
static class Node<E> {
E item;
Node<E> next;
Node(E x) { item = x; }
}
// 构造方法,默认无界队列(其实是有界的,Integer 最大值)
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
// 构造方法,可以指定队列大小
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity; // 队列大小
last = head = new Node<E>(null); // 初始就会创建一个虚拟节点,头结点和尾节点都会指向这个节点
}
// 构造方法
public LinkedBlockingQueue(Collection<? extends E> c) { }
// 内部添加元素的方法(私有)
private void enqueue(Node<E> node) {
last = last.next = node;
}
// 内部移除元素的方法(私有)
private E dequeue() {
Node<E> h = head;
Node<E> first = h.next;
h.next = h; // help GC
head = first;
E x = first.item;
first.item = null; // 新的头结点要作为虚拟节点,同 AQS 虚拟节点的线程要置为空的思想一致
return x;
}
// 阻塞入队
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
int c = -1;
Node<E> node = new Node<E>(e); // 入队的元素包装成 Node
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly(); // 获取入队锁
try {
while (count.get() == capacity) { // 如果队列满了就阻塞(线程进入条件队列)
notFull.await();
}
enqueue(node); // 队列未满,元素入队
c = count.getAndIncrement(); // 入队后维护元素个数。c = 旧值,count+=1
if (c + 1 < capacity) // 队列未满时,唤醒一个等待 notFull 的线程(唤醒生产者,唤醒的不是消费者,生产者唤醒生产者)
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0) // 如果原队列为空,这里刚添加了一个元素,唤醒一个消费者
signalNotEmpty();
}
// 阻塞出队
public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly(); // 获取出队锁
try {
while (count.get() == 0) { // 如果队列为空阻塞住
notEmpty.await();
}
x = dequeue(); // 队列不为空,元素出队,并得到出队的元素(移除都节点,第二个节点作为新的虚拟头结点)
c = count.getAndDecrement(); // c = 旧的元素个数,count-1
if (c > 1) // 如果队列还有元素,唤醒一个消费者(这里也是消费者唤醒消费者)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity) // 如果操作前队列是满的,唤醒一个消费者
signalNotFull();
return x; // 返回移除的元素
}
}
特性总结
- 基于链表实现的可选有界阻塞队列(默认
Integer.MAX_VALUE,可指定大小) - FIFO 顺序
- 吞吐量通常比
ArrayBlockingQueue高(两把锁分离读写) - 固定大小,不会扩容
(3) PriorityBlockingQueue
- 基于堆实现的无界优先级阻塞队列(默认自然排序,可自定义
Comparator)。 - 不保证 FIFO,按优先级出队。
- 适用于任务调度(如高优先级任务优先处理)。
BlockingQueue<Integer> queue = new PriorityBlockingQueue<>();
queue.put(3);
queue.put(1); // 1 会比 3 先出队
int num = queue.take(); // 1
(4) SynchronousQueue
- 不存储元素的阻塞队列(容量为 0)。
- 插入操作必须等待移除操作(配对机制)。
- 适用于直接传递任务(如
Executors.newCachedThreadPool)。
BlockingQueue<Integer> queue = new SynchronousQueue<>();
// 生产者线程
new Thread(() -> {
queue.put(1); // 阻塞,直到消费者调用 take()
}).start();
// 消费者线程
new Thread(() -> {
int num = queue.take(); // 取出 1
}).start();
(5) DelayQueue
- 基于
PriorityQueue的无界延迟队列(元素需实现Delayed接口)。 - 元素只有到期(
getDelay() <= 0)才能被取出。 - 适用于定时任务、缓存过期。
import java.util.concurrent.*;
// 1. 创建一个延迟元素(实现Delayed接口)
class SimpleDelayedItem implements Delayed {
private final String data;
private final long expireTime; // 到期时间(纳秒)
public SimpleDelayedItem(String data, long delayMs) {
this.data = data;
this.expireTime = System.nanoTime() + TimeUnit.NANOSECONDS.convert(delayMs, TimeUnit.MILLISECONDS);
}
// 2. 实现getDelay()方法(返回剩余延迟时间)
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(expireTime - System.nanoTime(), TimeUnit.NANOSECONDS);
}
// 3. 实现compareTo()方法(用于排序)
@Override
public int compareTo(Delayed o) {
return Long.compare(this.expireTime, ((SimpleDelayedItem)o).expireTime);
}
@Override
public String toString() {
return data;
}
}
public class SimpleDelayQueueDemo {
public static void main(String[] args) throws InterruptedException {
// 4. 创建DelayQueue
DelayQueue<SimpleDelayedItem> queue = new DelayQueue<>();
// 5. 添加延迟元素(2秒、1秒、3秒后到期)
queue.put(new SimpleDelayedItem("Item1", 2000));
queue.put(new SimpleDelayedItem("Item2", 1000));
queue.put(new SimpleDelayedItem("Item3", 3000));
// 6. 取出元素(按到期顺序)
System.out.println(queue.take()); // 1秒后输出 Item2
System.out.println(queue.take()); // 再等1秒输出 Item1
System.out.println(queue.take()); // 再等1秒输出 Item3
}
}
3. 如何选择合适的 BlockingQueue?
| 场景 | 推荐实现 |
|---|---|
| 固定大小的任务缓冲 | ArrayBlockingQueue |
| 高吞吐量任务队列 | LinkedBlockingQueue |
| 优先级任务调度 | PriorityBlockingQueue |
| 直接传递任务(无缓冲) | SynchronousQueue |
| 延迟/定时任务 | DelayQueue |
4. 总结
BlockingQueue是线程安全的阻塞队列,适用于生产者-消费者模型。ArrayBlockingQueue和LinkedBlockingQueue最常用,前者有界,后者可选有界,后者两把锁并发量更高。PriorityBlockingQueue和DelayQueue适用于特殊排序需求。SynchronousQueue和LinkedTransferQueue适用于直接交互场景。

浙公网安备 33010602011771号