linkedblockingqueue 通过分离的 putlock 和 takelock 双锁机制、各自关联的 notfull/notempty 条件队列,以及 atomicinteger 类型的 count,实现生产与消费操作无锁竞争、互不阻塞,从根本上避免死锁。

LinkedBlockingQueue 的 put 和 take 方法并未使用“独立重入锁”来防死锁,而是通过**两把完全分离的可重入锁(putLock 和 takeLock)配合条件队列**,实现生产与消费操作的**无锁竞争、互不阻塞**,从而天然规避了死锁风险。
双锁分离:putLock 与 takeLock 各司其职
LinkedBlockingQueue 内部维护两个 ReentrantLock 实例:
-
putLock:仅用于控制入队(put、offer等)操作,保护队尾(tail)和容量相关状态; -
takeLock:仅用于控制出队(take、poll等)操作,保护队头(head)和元素读取逻辑。
二者完全独立,不会嵌套获取,也不存在“先拿 A 再等 B”或“先拿 B 再等 A”的调用链,从根本上消除循环等待条件 —— 死锁四要素之一即被打破。
条件变量解耦等待:notFull 与 notEmpty 各自唤醒
每把锁都关联一个专属条件队列:
-
putLock.newCondition()→notFull:当队列满时,put线程在此 await;有线程take后触发notFull.signal()唤醒等待入队者; -
takeLock.newCondition()→notEmpty:当队列空时,take线程在此 await;有线程put后触发notEmpty.signal()唤醒等待出队者。
这种设计让等待/唤醒严格绑定在对应锁上,避免跨锁 signal 或 await,进一步杜绝因条件变量误用引发的同步异常或假死锁。
size 的原子更新:避免锁升级与竞态
队列长度 count 是一个 AtomicInteger,而非靠某一把锁保护:
-
put成功后执行count.incrementAndGet(); -
take成功后执行count.decrementAndGet(); - 判断满/空时(如
isFull()/isEmpty())直接读取count.get()。
这避免了为读取 size 而争夺任一锁,也不需要在双锁间协调计数状态,减少锁持有时间与依赖路径。
操作流程无交叉持锁:典型场景验证
以“满队列 put 阻塞 + 另一线程 take”为例:
- 线程 T1 调用
put→ 获取putLock→ 发现满 →notFull.await()(释放putLock,进入等待); - 线程 T2 调用
take→ 获取takeLock→ 取出元素 →count.decrementAndGet()→notFull.signal()(唤醒 T1); - T1 被唤醒后重新竞争
putLock,成功后继续入队。
整个过程,T1 永远只涉及 putLock,T2 永远只涉及 takeLock,无任何时刻同时持有两把锁,也无锁顺序依赖。
不复杂但容易忽略:它的防死锁不是靠“重入”特性,而是靠职责隔离 + 锁解耦 + 原子计数 —— 这才是高并发队列稳健的核心设计。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











