当前位置: 首页 > news >正文

Condition 底层实现深度解析:从源码看线程协作的艺术

Condition 底层实现深度解析:从源码看线程协作的艺术

一、为什么需要 Condition?

在 Java 并发编程中,等待/通知机制是多线程协作的核心模式。在Lock出现之前,我们依赖synchronized+Object.wait()/notify()实现这一模式。但这种方式存在明显局限:

  • 单等待队列:一个对象只能维护一个等待队列
  • 无法精准唤醒notify()随机唤醒,难以实现复杂的多条件协作
  • 功能受限:不支持超时等待、不响应中断等高级特性

Condition的出现正是为了解决这些问题。作为Lock的"伴侣",它提供了多条件队列更精细的线程控制能力


二、Object vs Condition:核心差异对比

特性Object 监视器Condition
前置条件获取对象监视器锁获取 Lock 锁 + 创建 Condition 对象
等待队列数量仅 1 个支持多个
超时等待支持支持
绝对时间等待不支持支持(awaitUntil)
不响应中断不支持支持(awaitUninterruptibly)
唤醒策略随机/全部精准唤醒指定条件队列

关键洞察:Condition 将"锁"与"条件"解耦。一个 Lock 可以绑定多个 Condition,每个 Condition 管理自己的等待队列,这让多条件复杂协作成为可能。


三、从使用到原理:层层深入

3.1 基础使用范式

publicclassConditionDemo{privatefinalLocklock=newReentrantLock();// 关键:Condition 必须依附于 LockprivatefinalConditioncondition=lock.newCondition();privatevolatilebooleanflag=false;publicvoidawaitTask(){lock.lock();try{while(!flag){// 必须用 while 防止虚假唤醒System.out.println(Thread.currentThread().getName()+" 进入等待");condition.await();// 释放锁 + 进入等待队列}System.out.println(Thread.currentThread().getName()+" 被唤醒,继续执行");}catch(InterruptedExceptione){Thread.currentThread().interrupt();}finally{lock.unlock();}}publicvoidsignalTask(){lock.lock();try{Thread.sleep(2000);// 模拟业务处理flag=true;System.out.println(Thread.currentThread().getName()+" 发送信号");condition.signal();// 唤醒等待队列首节点}catch(InterruptedExceptione){e.printStackTrace();}finally{lock.unlock();// 释放锁后,被唤醒线程才能竞争锁}}}

执行时序解析

Thread-0: 获取锁 → 条件不满足 → await() → 释放锁 → 阻塞等待 Thread-1: 获取锁 → 修改条件 → signal() → 释放锁 Thread-0: 被唤醒 → 竞争锁 → 获取锁 → 从 await() 返回 → 继续执行

3.2 核心方法速查

方法说明
await()释放锁,进入等待队列,直到被唤醒或中断
await(long time, TimeUnit unit)超时等待,返回是否超时
awaitNanos(long nanosTimeout)纳秒级超时,返回剩余时间
awaitUntil(Date deadline)直到指定时间点
awaitUninterruptibly()不响应中断,直到被唤醒
signal()唤醒一个等待线程(首节点)
signalAll()唤醒所有等待线程

四、源码深度剖析:AQS 的 ConditionObject

Condition的唯一实现是AQS的内部类ConditionObject。理解它,关键在于理解两个队列的协作

4.1 数据结构:等待队列 vs 同步队列

┌─────────────────────────────────────────────────────────────┐ │ AQS 内部结构 │ ├─────────────────────────────────────────────────────────────┤ │ 同步队列 (Sync Queue) 等待队列 (Wait Queue) │ │ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │ │ │head │ ←→ │node │ ←→ │node │ │first│ → │next │ → ... │ │ │ │ │ │ │tail │ │ │ │ │ │ │ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ │ │ (双向链表,竞争锁) (单向链表,条件等待) │ └─────────────────────────────────────────────────────────────┘

关键区别

  • 同步队列:双向链表,存储竞争锁的线程,使用prevnext
  • 等待队列:单向链表,存储条件等待的线程,使用nextWaiter

4.2 await() 方法:从"同步"到"等待"的旅程

publicfinalvoidawait()throwsInterruptedException{// 1. 响应中断if(Thread.interrupted())thrownewInterruptedException();// 2. 将当前线程包装为 Node,加入等待队列尾部Nodenode=addConditionWaiter();// 3. 完全释放锁,保存状态用于后续恢复intsavedState=fullyRelease(node);intinterruptMode=0;// 4. 核心循环:检查是否在同步队列中while(!isOnSyncQueue(node)){LockSupport.park(this);// 挂起线程// 5. 检查中断状态,决定后续处理if((interruptMode=checkInterruptWhileWaiting(node))!=0)break;}// 6. 被唤醒后:竞争锁(在同步队列中)if(acquireQueued(node,savedState)&&interruptMode!=THROW_IE)interruptMode=REINTERRUPT;// 7. 清理已取消的节点if(node.nextWaiter!=null)unlinkCancelledWaiters();// 8. 处理中断if(interruptMode!=0)reportInterruptAfterWait(interruptMode);}

执行流程图解

获取锁的线程 │ ▼ 调用 await() │ ├──► 创建 Node(waitStatus=CONDITION) │ ├──► 加入等待队列尾部 │ │ │ ▼ │ ┌─────────┐ ┌─────────┐ │ │ ThreadA │───►│ ThreadB │───► ... │ │ (新加入)│ │ (等待中)│ │ └─────────┘ └─────────┘ │ ├──► 释放锁 fullyRelease() │ │ │ └──► 唤醒同步队列后继节点 │ ├──► 挂起 LockSupport.park() │ ▼ 等待被 signal / 中断
4.2.1 加入等待队列:addConditionWaiter()
privateNodeaddConditionWaiter(){Nodet=lastWaiter;// 清理已取消的节点(waitStatus != CONDITION)if(t!=null&&t.waitStatus!=Node.CONDITION){unlinkCancelledWaiters();t=lastWaiter;}// 创建 CONDITION 状态的节点Nodenode=newNode(Thread.currentThread(),Node.CONDITION);// 尾插入if(t==null)firstWaiter=node;elset.nextWaiter=node;lastWaiter=node;returnnode;}

注意:等待队列是不带头节点的单向链表,这与同步队列(带头节点的双向链表)不同。

4.2.2 释放锁:fullyRelease()
finalintfullyRelease(Nodenode){booleanfailed=true;try{intsavedState=getState();// 保存重入次数if(release(savedState)){// 调用 AQS 释放锁failed=false;returnsavedState;// 返回保存的状态}else{thrownewIllegalMonitorStateException();}}finally{if(failed)node.waitStatus=Node.CANCELLED;// 释放失败则标记取消}}

为什么保存savedState因为锁可能是重入的,后续重新获取锁时需要恢复到相同的重入次数。

4.2.3 退出条件:isOnSyncQueue()
finalbooleanisOnSyncQueue(Nodenode){// waitStatus 为 CONDITION,说明肯定在等待队列if(node.waitStatus==Node.CONDITION||node.prev==null)returnfalse;// 有后继节点,说明肯定在同步队列if(node.next!=null)returntrue;// 从尾部向前查找returnfindNodeFromTail(node);}

退出while循环的两种情况

  1. 被 signal:节点被移动到同步队列,isOnSyncQueue返回 true
  2. 被中断checkInterruptWhileWaiting返回非 0,执行break

4.3 signal() 方法:从"等待"到"同步"的转移

publicfinalvoidsignal(){// 必须持有锁才能调用if(!isHeldExclusively())thrownewIllegalMonitorStateException();Nodefirst=firstWaiter;if(first!=null)doSignal(first);// 处理首节点}privatevoiddoSignal(Nodefirst){do{// 移除首节点if((firstWaiter=first.nextWaiter)==null)lastWaiter=null;first.nextWaiter=null;}while(!transferForSignal(first)&&(first=firstWaiter)!=null);}

核心转移逻辑:transferForSignal()

finalbooleantransferForSignal(Nodenode){// 1. CAS 修改状态:CONDITION -> 0if(!compareAndSetWaitStatus(node,Node.CONDITION,0))returnfalse;// 修改失败说明节点已取消// 2. 加入同步队列尾部,返回前驱节点Nodep=enq(node);intws=p.waitStatus;// 3. 前驱节点已取消,或设置 SIGNAL 失败,立即唤醒if(ws>0||!compareAndSetWaitStatus(p,ws,Node.SIGNAL))LockSupport.unpark(node.thread);returntrue;}

转移过程图解

等待队列 (Wait Queue) 同步队列 (Sync Queue) ┌─────────┐ ┌─────────┐ ┌─────┐ ┌─────┐ ┌─────┐ │ ThreadA │───►│ ThreadB │ │head │ ←→ │node │ ←→ │tail │ │ (首节点)│ │ (等待中)│ │ │ │ │ │ │ └─────┬───┘ └─────────┘ └─────┘ └─────┘ └─────┘ │ │ signal() ▼ ┌─────────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │ ThreadB │ │head │ ←→ │node │ ←→ │tail │ ←→ │ThreadA│ │ (新首节点)│ │ │ │ │ │ │ │(转移来的)│ └─────────┘ └─────┘ └─────┘ └─────┘ └─────┘

关键细节

  • signal()只是将节点移动到同步队列,并不立即唤醒线程
  • 线程真正被唤醒是在释放锁之后unlock()
  • 唤醒后线程需要重新竞争锁,这可能再次阻塞

4.4 signalAll():批量转移

privatevoiddoSignalAll(Nodefirst){lastWaiter=firstWaiter=null;// 清空等待队列do{Nodenext=first.nextWaiter;first.nextWaiter=null;// 断开连接transferForSignal(first);// 逐个转移first=next;}while(first!=null);}

效果:将等待队列中的所有节点一次性转移到同步队列。


五、核心机制总结

5.1 等待/通知的完整流程

┌─────────────┐ lock() ┌─────────────┐ │ Thread A │ ◄────────────────────── │ Thread B │ │ (消费者) │ │ (生产者) │ └──────┬──────┘ └──────┬──────┘ │ │ │ await() │ signal() │ 1. 加入等待队列 │ 1. 将A从等待队列移除 │ 2. 释放锁 │ 2. 加入同步队列 │ 3. 挂起等待 │ 3. 唤醒A(如果必要) ▼ │ [等待状态] ◄───────────────────────────────────┘ │ │ 被唤醒后 ▼ [同步队列] ──► 竞争锁 ──► 获取锁 ──► 从await()返回

5.2 与 Object 监视器的本质区别

维度Object.wait/notifyCondition
队列模型1个等待队列多个条件队列+ 1个同步队列
线程状态流转等待队列 ↔ 就绪等待队列 ↔ 同步队列 ↔ 运行
唤醒精度粗糙(随机/全部)精准(指定条件队列)
扩展性固定功能支持超时、不中断等

六、最佳实践与常见陷阱

✅ 最佳实践

  1. 始终使用while循环检查条件

    while(!conditionMet){// 不要用 ifcondition.await();}
  2. 区分signal()signalAll()

    • 单一等待线程用signal()更高效
    • 多个等待线程可能都需要响应时用signalAll()
  3. finally中释放锁

    lock.lock();try{// 业务逻辑}finally{lock.unlock();// 确保释放}

❌ 常见陷阱

  1. synchronized中使用 Condition→ 会抛IllegalMonitorStateException
  2. 忘记检查中断状态→ 可能导致线程无法正确退出
  3. 使用if代替while→ 虚假唤醒导致逻辑错误
  4. signal()unlock()→ 虽然正确,但延迟了唤醒时机

七、结语

Condition的设计体现了“分离关注点”的思想:将"锁的管理"与"条件的等待"解耦,让多线程协作更加灵活和高效。

理解它的关键在于把握两个队列的转换

  • 等待队列是"休息室",线程在这里等待某个条件
  • 同步队列是"竞争场",线程在这里争夺锁的执行权

await()signal()就是在这两个队列之间搬运线程的"调度器",而LockSupport.park/unpark则是底层真正让线程睡/醒的"开关"。

http://www.jsqmd.com/news/418142/

相关文章:

  • 2026年金融科技平台行业影响力分析:头部平台认可度与贡献对比 - 速递信息
  • 产后贫血/怀孕贫血滋补保健品品牌怎么选?2026国内最新补血口服液五大品牌排名及解析 - 十大品牌榜
  • VS2026 离线安装闪退解决
  • 2026国内最新补血口服液五大品牌排名及解析 - 十大品牌榜
  • 2026年AI测试工具评测:谁在解决问题,谁在割韭菜?
  • 53453
  • 状态建图最短路
  • 2026广东最新天然野生沉香厂家直销优选指南 十大品质厂商参考 - 十大品牌榜
  • 题解:P15238 [NHSPC 2025] 电动车充电规划问题
  • 智慧农林多源数据预处理、高光谱AI智能精准提取、多模态模型构建、不确定性分析
  • E57格式:点云互作性指南e57/las/rcp/ply格式转换成su、skp、max,obj,fbx格式glb,gltf
  • 基于Python与AI的地球科学数据分析:植被动态、趋势归因与生态遥感评估
  • 深度挖掘遥感时空大数据价值、GeoAI可解释性建模与机理归因
  • ViCLIP-OT The First Foundation Vision-Language Model for Vietnamese Image-Text Retrieval with Optima
  • jenkins替换国内源方法
  • 基于Python与ArcGIS的碳水循环模拟、数据处理与多产品融合实践
  • 2026 2.27 模拟赛总结
  • 题解: P4233 射命丸文的笔记
  • SHMEM:CANN多设备高性能通信库正式开源
  • 260205
  • CiteLLM An Agentic Platform for Trustworthy Scientific Reference Discovery
  • LeetCode 393 UTF-8 编码验证
  • 鸿蒙应用如何高效管理后台任务,避免 CPU 资源浪费
  • D.二分查找-二分答案-其他——374. 猜数字大小
  • 大数据架构数据并行处理:任务拆分与负载均衡
  • 大数据领域中内存计算的网络传输优化
  • 有哪些靠谱的开题报告写作网站推荐
  • 好用的免费ai论文写作生成器(在线ai论文写作生成器)
  • 推荐几款知名的ai论文写作软件品牌
  • Netty中的ByteBuf