大连网站建设佳熙科技,兰州广告设计制作公司,把照片做册子的网站,中企动力z云邮箱登录Condition 底层实现深度解析#xff1a;从源码看线程协作的艺术 一、为什么需要 Condition#xff1f; 在 Java 并发编程中#xff0c;等待/通知机制是多线程协作的核心模式。在 Lock 出现之前#xff0c;我们依赖 synchronized Object.wait()/notify() 实现这一模式。但这…Condition 底层实现深度解析从源码看线程协作的艺术一、为什么需要 Condition在 Java 并发编程中等待/通知机制是多线程协作的核心模式。在Lock出现之前我们依赖synchronizedObject.wait()/notify()实现这一模式。但这种方式存在明显局限单等待队列一个对象只能维护一个等待队列无法精准唤醒notify()随机唤醒难以实现复杂的多条件协作功能受限不支持超时等待、不响应中断等高级特性Condition的出现正是为了解决这些问题。作为Lock的伴侣它提供了多条件队列和更精细的线程控制能力。二、Object vs Condition核心差异对比特性Object 监视器Condition前置条件获取对象监视器锁获取 Lock 锁 创建 Condition 对象等待队列数量仅 1 个支持多个超时等待支持支持绝对时间等待不支持支持(awaitUntil)不响应中断不支持支持(awaitUninterruptibly)唤醒策略随机/全部精准唤醒指定条件队列关键洞察Condition 将锁与条件解耦。一个 Lock 可以绑定多个 Condition每个 Condition 管理自己的等待队列这让多条件复杂协作成为可能。三、从使用到原理层层深入3.1 基础使用范式publicclassConditionDemo{privatefinalLocklocknewReentrantLock();// 关键Condition 必须依附于 LockprivatefinalConditionconditionlock.newCondition();privatevolatilebooleanflagfalse;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);// 模拟业务处理flagtrue;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 的 ConditionObjectCondition的唯一实现是AQS的内部类ConditionObject。理解它关键在于理解两个队列的协作。4.1 数据结构等待队列 vs 同步队列┌─────────────────────────────────────────────────────────────┐ │ AQS 内部结构 │ ├─────────────────────────────────────────────────────────────┤ │ 同步队列 (Sync Queue) 等待队列 (Wait Queue) │ │ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │ │ │head │ ←→ │node │ ←→ │node │ │first│ → │next │ → ... │ │ │ │ │ │ │tail │ │ │ │ │ │ │ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ │ │ (双向链表竞争锁) (单向链表条件等待) │ └─────────────────────────────────────────────────────────────┘关键区别同步队列双向链表存储竞争锁的线程使用prev和next等待队列单向链表存储条件等待的线程使用nextWaiter4.2 await() 方法从同步到等待的旅程publicfinalvoidawait()throwsInterruptedException{// 1. 响应中断if(Thread.interrupted())thrownewInterruptedException();// 2. 将当前线程包装为 Node加入等待队列尾部NodenodeaddConditionWaiter();// 3. 完全释放锁保存状态用于后续恢复intsavedStatefullyRelease(node);intinterruptMode0;// 4. 核心循环检查是否在同步队列中while(!isOnSyncQueue(node)){LockSupport.park(this);// 挂起线程// 5. 检查中断状态决定后续处理if((interruptModecheckInterruptWhileWaiting(node))!0)break;}// 6. 被唤醒后竞争锁在同步队列中if(acquireQueued(node,savedState)interruptMode!THROW_IE)interruptModeREINTERRUPT;// 7. 清理已取消的节点if(node.nextWaiter!null)unlinkCancelledWaiters();// 8. 处理中断if(interruptMode!0)reportInterruptAfterWait(interruptMode);}执行流程图解获取锁的线程 │ ▼ 调用 await() │ ├──► 创建 Node(waitStatusCONDITION) │ ├──► 加入等待队列尾部 │ │ │ ▼ │ ┌─────────┐ ┌─────────┐ │ │ ThreadA │───►│ ThreadB │───► ... │ │ (新加入)│ │ (等待中)│ │ └─────────┘ └─────────┘ │ ├──► 释放锁 fullyRelease() │ │ │ └──► 唤醒同步队列后继节点 │ ├──► 挂起 LockSupport.park() │ ▼ 等待被 signal / 中断4.2.1 加入等待队列addConditionWaiter()privateNodeaddConditionWaiter(){NodetlastWaiter;// 清理已取消的节点waitStatus ! CONDITIONif(t!nullt.waitStatus!Node.CONDITION){unlinkCancelledWaiters();tlastWaiter;}// 创建 CONDITION 状态的节点NodenodenewNode(Thread.currentThread(),Node.CONDITION);// 尾插入if(tnull)firstWaiternode;elset.nextWaiternode;lastWaiternode;returnnode;}注意等待队列是不带头节点的单向链表这与同步队列带头节点的双向链表不同。4.2.2 释放锁fullyRelease()finalintfullyRelease(Nodenode){booleanfailedtrue;try{intsavedStategetState();// 保存重入次数if(release(savedState)){// 调用 AQS 释放锁failedfalse;returnsavedState;// 返回保存的状态}else{thrownewIllegalMonitorStateException();}}finally{if(failed)node.waitStatusNode.CANCELLED;// 释放失败则标记取消}}为什么保存savedState因为锁可能是重入的后续重新获取锁时需要恢复到相同的重入次数。4.2.3 退出条件isOnSyncQueue()finalbooleanisOnSyncQueue(Nodenode){// waitStatus 为 CONDITION说明肯定在等待队列if(node.waitStatusNode.CONDITION||node.prevnull)returnfalse;// 有后继节点说明肯定在同步队列if(node.next!null)returntrue;// 从尾部向前查找returnfindNodeFromTail(node);}退出while循环的两种情况被 signal节点被移动到同步队列isOnSyncQueue返回 true被中断checkInterruptWhileWaiting返回非 0执行break4.3 signal() 方法从等待到同步的转移publicfinalvoidsignal(){// 必须持有锁才能调用if(!isHeldExclusively())thrownewIllegalMonitorStateException();NodefirstfirstWaiter;if(first!null)doSignal(first);// 处理首节点}privatevoiddoSignal(Nodefirst){do{// 移除首节点if((firstWaiterfirst.nextWaiter)null)lastWaiternull;first.nextWaiternull;}while(!transferForSignal(first)(firstfirstWaiter)!null);}核心转移逻辑transferForSignal()finalbooleantransferForSignal(Nodenode){// 1. CAS 修改状态CONDITION - 0if(!compareAndSetWaitStatus(node,Node.CONDITION,0))returnfalse;// 修改失败说明节点已取消// 2. 加入同步队列尾部返回前驱节点Nodepenq(node);intwsp.waitStatus;// 3. 前驱节点已取消或设置 SIGNAL 失败立即唤醒if(ws0||!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){lastWaiterfirstWaiternull;// 清空等待队列do{Nodenextfirst.nextWaiter;first.nextWaiternull;// 断开连接transferForSignal(first);// 逐个转移firstnext;}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个同步队列线程状态流转等待队列 ↔ 就绪等待队列 ↔ 同步队列 ↔ 运行唤醒精度粗糙随机/全部精准指定条件队列扩展性固定功能支持超时、不中断等六、最佳实践与常见陷阱✅ 最佳实践始终使用while循环检查条件while(!conditionMet){// 不要用 ifcondition.await();}区分signal()和signalAll()单一等待线程用signal()更高效多个等待线程可能都需要响应时用signalAll()在finally中释放锁lock.lock();try{// 业务逻辑}finally{lock.unlock();// 确保释放}❌ 常见陷阱在synchronized中使用 Condition→ 会抛IllegalMonitorStateException忘记检查中断状态→ 可能导致线程无法正确退出使用if代替while→ 虚假唤醒导致逻辑错误先signal()后unlock()→ 虽然正确但延迟了唤醒时机七、结语Condition的设计体现了“分离关注点”的思想将锁的管理与条件的等待解耦让多线程协作更加灵活和高效。理解它的关键在于把握两个队列的转换等待队列是休息室线程在这里等待某个条件同步队列是竞争场线程在这里争夺锁的执行权await()和signal()就是在这两个队列之间搬运线程的调度器而LockSupport.park/unpark则是底层真正让线程睡/醒的开关。