arrayblockingqueue 的 put 方法通过 while 循环检查队列是否已满,若满则调用 notfull.await() 释放锁并阻塞,待 notfull.signal() 唤醒后重新抢锁并再次验证条件再插入元素。

Java 中 BlockingQueue 接口的实现类(如 ArrayBlockingQueue、LinkedBlockingQueue)通过 ReentrantLock 配合 Condition 实现线程安全的阻塞等待,put 和 take 方法正是基于条件队列(Condition)完成“满时阻塞生产者”和“空时阻塞消费者”的语义。
put 方法如何用条件队列阻塞等待队列不满
put(E e) 在队列满时不会直接返回或抛异常,而是挂起当前线程,直到有空间可用。其核心逻辑是:
- 获取独占锁(
ReentrantLock.lock()),保证对队列状态的互斥访问 - 循环检查队列是否已满;若满,调用
notFull.await()进入该条件队列并释放锁,线程进入 WAITING 状态 - 当其他线程调用
take或poll取出元素后,会触发notFull.signal()唤醒一个等待的生产者线程 - 被唤醒的线程重新竞争锁,再次检查条件(防止虚假唤醒),满足则插入元素并返回
take 方法如何用条件队列阻塞等待队列不空
take() 在队列为空时同样阻塞,依赖另一个条件对象 notEmpty:
- 加锁后检查队列是否为空;若空,调用
notEmpty.await(),线程进入notEmpty条件队列并释放锁 - 当其他线程执行
put或offer成功插入元素后,会调用notEmpty.signal()唤醒一个等待的消费者 - 被唤醒线程重新持锁,再次验证队列非空(防御性检查),然后取出并返回头元素
两个条件队列共存于同一把锁下
ArrayBlockingQueue 内部只使用一个 ReentrantLock,但创建了两个独立的 Condition 实例:
final Condition notEmpty = lock.newCondition();final Condition notFull = lock.newCondition();
这使得“等待非空”和“等待非满”可解耦唤醒:消费者只响应 notEmpty.signal(),生产者只响应 notFull.signal(),避免无差别唤醒(signalAll)带来的性能浪费和线程竞争。
注意虚假唤醒与循环检查的必要性
即便没有显式 signal,JVM 也可能唤醒等待线程(虚假唤醒)。因此,所有 await 调用都必须包裹在 while 循环中重新验证条件:
// 伪代码示意(实际源码类似)while (queue.isFull()) {<br> notFull.await();<br>}
跳过循环检查可能导致线程在不满足业务条件时继续执行,引发 IllegalStateException 或数据错乱。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











