极地冰盖监测:GLM-4.6V-Flash-WEB计算融化面积
2026/1/5 18:57:09
ReentrantLock,一次只允许一个线程持有锁。unparkSuccessor(head)即可)。Semaphore、CountDownLatch、ReadWriteLock的读锁。这就引出了“传播(propagation)”的概念。
假设:
但如果只在释放时唤醒一次(像独占锁那样),B 和 C 就可能永远等下去,即使资源已经可用。
👉 因此,共享模式必须支持“级联唤醒”或“传播唤醒”。
Node.SIGNAL(-1)SIGNAL,表示“我等着,你释放时记得叫我”。Node.PROPAGATE(-3)考虑这个竞态条件(race condition):
releaseShared(),准备唤醒后继。ws == 0,于是尝试 CAS 把它设为PROPAGATE(表示:“虽然现在没信号,但我要记录这次释放,以便后续传播”)。setHeadAndPropagate,把新节点设为 head。PROPAGATE,T1 可能认为“没人需要唤醒”,直接退出,导致后面的线程无法被唤醒!✅PROPAGATE的作用就是:在状态不确定时,留下一个“释放发生过”的标记,确保后续操作能继续传播唤醒。
setHeadAndPropagate(node, propagate)propagate > 0:表示tryAcquireShared返回正数,说明还有剩余资源,应该继续唤醒别人。if(propagate>0||h==null||h.waitStatus<0||...)只要有任何迹象表明可能需要传播,就调用doReleaseShared()。h.waitStatus < 0包括SIGNAL(-1)和PROPAGATE(-3),都表示“需要关注后继”。doReleaseShared()这是一个自旋 + CAS 的传播循环:
for(;;){Nodeh=head;if(h!=null&&h!=tail){intws=h.waitStatus;if(ws==SIGNAL){// 正常情况:后继需要唤醒CAS(SIGNAL→0);unparkSuccessor(h);}elseif(ws==0){// 没有明确信号,但可能是并发释放!// 设为 PROPAGATE,留下“释放已发生”的标记CAS(0→PROPAGATE);}}// 如果 head 没变,说明稳定了,可以退出if(h==head)break;}💡 关键思想:即使当前看不出需要唤醒谁,也要通过 PROPAGATE 确保“释放事件”不会丢失。
Semaphoresem=newSemaphore(1);// 三个线程同时调用 sem.acquire()// 初始 permit = 1,只有第一个能成功,其他两个入队等待。sem.release();// permit 变回 1release()→ 调用releaseShared()→doReleaseShared()propagate = 0(因为 permit 用完了),不传播release()被调用了两次(permit=2),则:propagate=1→ 继续传播doReleaseShared(),唤醒 B如果没有传播机制,B 就卡住了!
| 概念 | 作用 |
|---|---|
| 传播(Propagation) | 共享模式下,一次释放可能需唤醒多个线程 |
| SIGNAL (-1) | 明确指示“后继需要唤醒” |
| PROPAGATE (-3) | 在状态模糊时,防止释放信号丢失的保险机制 |
| 自旋 + CAS 循环 | 应对高并发下的状态竞争,确保最终一致性 |
✅设计哲学:
“宁可多唤醒几次(unnecessary wake-ups),也不能漏掉一次该唤醒的线程。”
—— 这就是 AQS 共享模式的鲁棒性所在。
| 值 | 常量 | 含义 |
|---|---|---|
| 0 | — | 初始状态 |
| -1 | SIGNAL | 后继需要被唤醒 |
| -2 | CONDITION | 在 Condition 队列中 |
| -3 | PROPAGATE | 共享模式下,表示应继续传播释放 |
| 1 | CANCELLED | 节点已取消 |
希望这能帮你彻底理解 AQS 共享模式的“传播”机制!