acquireshared根据tryacquireshared返回值决定是否入队:负值失败入队,非负值表示成功或可传播唤醒,不入队。

acquireShared 为什么返回值决定是否入队
acquireShared 的核心逻辑是先调用 tryAcquireShared,再根据返回值判断后续动作。返回值含义很关键:负数 表示获取失败(资源耗尽),0 表示刚好拿完(无剩余),正数 表示成功且还有余量。只有返回值 才会进入 <code>doAcquireShared 构建等待节点并入队。这意味着:不是“只要有线程在等就唤醒”,而是“只要还有剩余资源,就继续放行”,避免无谓的唤醒开销。
setHeadAndPropagate 是级联唤醒的真正触发点
当一个线程成功获取资源后,setHeadAndPropagate(node, r) 被调用。这里的 r 就是 tryAcquireShared 的返回值——它代表**本次获取后还剩多少许可**。该方法不仅把当前节点设为新 head,还会检查是否需要向后传播唤醒:
• 如果 r > 0(比如初始 permits=5,当前线程只取了1,还剩4),大概率会唤醒后继共享节点;
• 如果 r == 0(刚好取完),是否唤醒取决于前驱 head 的 waitStatus 是否为 SIGNAL 或 h == null 等边界条件;
• 它不是“唤醒一个”,而是“尝试唤醒一个,该线程醒来后自己再决定要不要继续唤醒下一个”。
doReleaseShared 如何避免唤醒风暴
release() 最终走到 doReleaseShared(),这是共享模式下真正的唤醒中枢。它用 CAS 循环尝试将 state 增加(归还许可),并在成功后:
• 检查后继节点是否为 SHARED 模式;
• 若是,调用 unparkSuccessor 唤醒它;
• **关键点**:它不保证“一次 release 唤醒所有等待者”,而是“唤醒一个,由那个线程在 setHeadAndPropagate 中自行判断是否继续传播”。这防止了高并发释放时大量线程同时被 unpark 导致的调度风暴。
非公平模式下唤醒顺序与 acquire 顺序无关
默认构造 new Semaphore(permits) 使用的是非公平策略,nonfairTryAcquireShared 总是直接 CAS 尝试获取,不检查队列。这就意味着:
• 刚 release 归还许可后被唤醒的线程,可能还没来得及 tryAcquireShared,另一个新来的线程就抢先拿到了许可;
• 级联唤醒链可能被“插队”打断,实际唤醒顺序 ≠ 入队顺序;
• 如果业务强依赖 FIFO,必须显式使用 new Semaphore(permits, true) 启用公平模式,但会带来额外的 head 判断开销。
r 主动决定是否推动下一轮传播——这个设计让唤醒行为天然具备节制性和上下文感知能力,但也意味着你不能假设 release() 一定会唤醒全部阻塞线程。











