用condition实现高性能并发阻塞队列,需分别用notempty和notfull两个condition精准控制消费者等待(队列空)与生产者等待(队列满),配合reentrantlock、while循环防虚假唤醒、volatile size优化及短临界区设计。
用 condition 实现高性能并发阻塞队列,核心在于精准控制“非空”和“非满”两个等待条件,避免无谓的唤醒与竞争,比 synchronized + wait/notify 更灵活、更轻量。
明确两个 Condition 分别管什么
一个 Condition 用于消费者等待——队列为空时阻塞;另一个用于生产者等待——队列满时阻塞。不能共用同一个 Condition,否则 signalAll() 会误唤醒所有线程,引发无效竞争。
-
notEmpty:消费者调用
take()时,若size == 0,就await()在这个条件上 -
notFull:生产者调用
put()时,若size == capacity,就await()在这个条件上 - 每次成功插入后,只
signal(notEmpty);每次成功移除后,只signal(notFull)
用 ReentrantLock 替代 synchronized,支持可中断与公平策略
ReentrantLock 提供了 newCondition() 方法,且能配合 lockInterruptibly() 实现响应中断的阻塞,这对高可靠性系统很关键。默认非公平锁性能更好,除非业务强依赖 FIFO 入队顺序才启用公平模式。
- 构造时传入
capacity,内部用数组或链表存储元素(数组实现更紧凑,适合固定容量) - 所有读写操作必须在
lock.lock()/unlock()块内完成,await()前已持锁,await()会自动释放锁,唤醒后重新竞争锁 - 推荐在
finally中unlock(),防止异常导致死锁
避免虚假唤醒,while 循环检查条件
Condition.await() 可能被信号中断、超时或虚假唤醒,所以必须用 while 而不是 if 判断条件是否真正满足。
-
put()中:while (size == capacity) notFull.await(); -
take()中:while (size == 0) notEmpty.await(); - 即使被唤醒,也要再次校验状态,确保逻辑安全
提升吞吐的关键细节
高性能不只靠锁粒度,还取决于减少上下文切换和内存可见性开销:
- 用
volatile int size或AtomicInteger记录当前元素数,避免每次都要加锁读取(但修改仍需锁保护) - 数组实现时,用两个指针(
head、tail)配合取模运算,避免频繁对象创建;链表实现则注意Node对象复用 -
offer()和poll()可做成非阻塞版本,直接返回false而不等待,适应不同场景 - 避免在锁内做耗时操作(如 IO、复杂计算),保持临界区短小
不复杂但容易忽略。关键是把“谁等什么条件”想清楚,再用 while + signal 精准驱动,锁只是保障状态变更的原子性,不是万能同步器。











