ArrayBlockingQueue 是 Java 并发包中的一个线程安全、基于数组实现的有界阻塞队列,适用于生产者-消费者模型。以下是其核心特性及工作原理的通俗解析:
一、核心特点
-
有界队列
- 创建时需指定固定容量,队列满时无法添加新元素,避免无限制内存消耗。
- 示例:
new ArrayBlockingQueue<>(100);表示最多容纳100个元素。
-
阻塞操作
- 队列满时:生产者线程调用
put()会被阻塞,直到队列有空位。 - 队列空时:消费者线程调用
take()会被阻塞,直到队列有新元素。
- 队列满时:生产者线程调用
-
FIFO 顺序
- 先进先出,保证元素按入队顺序处理。
-
线程安全
- 内部通过 ReentrantLock 和 Condition 机制实现同步。
二、底层实现
-
数据结构
- 循环数组:元素存储在固定大小的数组中,通过头尾指针实现循环利用。
- 示例:当数组尾部填满时,新元素会插入到数组头部(若有空位)。
-
同步机制
-
一把锁(ReentrantLock) :控制所有入队和出队操作。
-
两个条件变量(Condition) :
- notEmpty:队列非空时唤醒消费者。
- notFull:队列非满时唤醒生产者。
-
三、核心方法
| 方法 | 行为 |
|---|---|
put(E e) | 队列满时阻塞,直到有空位插入。 |
take() | 队列空时阻塞,直到有元素可取出。 |
offer(E e) | 队列满时直接返回 false,不阻塞。 |
poll() | 队列空时直接返回 null,不阻塞。 |
四、适用场景
-
固定资源池
-
如数据库连接池,控制同时使用的连接数。
BlockingQueue<Connection> pool = new ArrayBlockingQueue<>(10); -
-
流量削峰
-
缓冲突发请求,防止系统过载。
// 生产者快速接收请求,消费者按处理能力消费 BlockingQueue<Request> queue = new ArrayBlockingQueue<>(1000); -
-
任务调度
-
多线程间传递任务,确保顺序执行。
ExecutorService executor = Executors.newFixedThreadPool(4); BlockingQueue<Runnable> taskQueue = new ArrayBlockingQueue<>(100); -
五、性能与权衡
| 优点 | 缺点 |
|---|---|
| 内存紧凑(数组连续存储) | 并发性能较低(单锁机制) |
| 避免动态扩容开销 | 容量固定,需提前合理规划 |
| 严格FIFO顺序 | 高竞争下吞吐量不如双锁队列 |
六、与 LinkedBlockingQueue 对比
| 对比项 | ArrayBlockingQueue | LinkedBlockingQueue |
|---|---|---|
| 底层结构 | 数组 | 链表 |
| 锁机制 | 单锁(生产消费互斥) | 双锁(生产消费可并行) |
| 内存占用 | 更小(无节点指针开销) | 更大(每个元素需额外指针) |
| 吞吐量 | 低至中(单锁) | 高(双锁并发) |
七、代码示例
public class ProducerConsumerExample {
public static void main(String[] args) {
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);
// 生产者
new Thread(() -> {
try {
for (int i = 0; i < 20; i++) {
queue.put(i); // 队列满时阻塞
System.out.println("Produced: " + i);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
// 消费者
new Thread(() -> {
try {
for (int i = 0; i < 20; i++) {
int value = queue.take(); // 队列空时阻塞
System.out.println("Consumed: " + value);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
}
八、总结
ArrayBlockingQueue 是一个简单可靠的有界阻塞队列,适合对内存敏感、需严格容量控制的场景。其单锁机制虽限制了并发性能,但通过条件变量实现了高效的线程协作。在设计中需权衡容量与吞吐量需求,避免队列成为系统瓶颈。
使用口诀:
「数组实现容量固,生产消费互阻塞
单锁控制保安全,条件变量唤线程
内存紧凑性能中,适用流量削峰场!」