arrayblockingqueue采用“一个锁+两个condition”的协作模型:notempty用于消费者等待非空,notfull用于生产者等待非满;二者共享同一reentrantlock,确保状态判断与等待/唤醒原子性,signal操作在状态变更后、解锁前执行,避免虚假唤醒和唤醒丢失。

ArrayBlockingQueue 使用两个 Condition 对象 —— notEmpty 和 notFull —— 来协调生产者与消费者线程的等待与唤醒,核心在于“一个锁 + 两个条件队列”的协作模型。
notEmpty 用于消费者等待非空状态
当队列为空时,调用 take() 或带超时的 poll(long, TimeUnit) 的消费者线程会被挂起在 notEmpty 条件队列上。只有在有元素入队(即 enqueue 成功)后,生产者线程显式调用 notEmpty.signal() 才会唤醒等待的消费者。
- 唤醒不保证立即消费:
signal()只是将线程移入 AQS 同步队列,需重新竞争主锁(ReentrantLock),获取锁后才真正执行取值逻辑 - 使用
signalAll()的场景极少:通常单个消费者被唤醒即可,避免不必要的上下文切换;仅在批量唤醒有意义时(如中断全部等待者)才用
notFull 用于生产者等待非满状态
当队列已满时,调用 put() 或 offer(...) 的生产者线程会阻塞在 notFull 条件队列上。一旦有元素被消费(dequeue 完成),消费者线程调用 notFull.signal() 唤醒等待的生产者。
- 注意信号时机:唤醒操作总是在修改队列结构(如成功入队/出队)并释放锁前完成,确保状态一致性
- 不会虚假唤醒:基于
ReentrantLock的Condition实现天然防止虚假唤醒,但代码中仍应使用 while 循环检查条件(如while (count == items.length)),这是标准防护习惯
两个 Condition 共享同一把锁
notEmpty 和 notFull 都由同一个 ReentrantLock 实例创建,因此它们共用同一同步机制。这保证了:
- 所有队列操作(增、删、查)都必须先获取该锁,避免并发修改导致的数据错乱
- 条件等待和唤醒都在锁保护下进行,状态判断(如
count == 0)与挂起/唤醒是原子的 - 不会出现“唤醒丢失”:因为
signal()总是在持有锁时调用,而等待线程只在释放锁后进入条件队列,时序受锁严格约束
典型协作流程示例
以 put(e) 和 take() 为例:
-
put():加锁 → 若满,notFull.await()(释放锁并挂起)→ 被唤醒后重入锁 → 入队 →notEmpty.signal()→ 解锁 -
take():加锁 → 若空,notEmpty.await()→ 被唤醒后重入锁 → 出队 →notFull.signal()→ 解锁 - 关键点:每次 signal 都发生在状态变更之后、解锁之前,且只唤醒一个线程,兼顾效率与公平性
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











