tryAcquire() 返回 false 仅表示当前瞬时无法获取许可,不等于无空闲许可;它不排队、不等待、不保证公平性,每次调用均为独立快照,需配对 release() 并推荐 try-finally 确保不泄漏。

tryAcquire() 不返回 true 就代表没拿到许可?
不是。它只代表「此刻无法立即获取」——可能因为许可已被用完,也可能因线程调度延迟导致短暂竞争失败。关键在于:tryAcquire() 是瞬时判断,不排队、不等待、不参与公平性排序。哪怕你刚释放一个许可,如果此时有其他线程正巧调用 tryAcquire() 并抢先抢到,你的调用就会返回 false。
常见错误是把它当「检查是否还有空闲许可」用,比如:
if (sem.tryAcquire()) {
// 假设这里一定有许可可用 → 错!
doWork();
sem.release();
}
这逻辑本身没错,但容易忽略:即使 tryAcquire() 返回 true,也不等于后续操作一定能完成(比如 doWork() 抛异常后忘记 release(),就会永久泄漏许可)。
- 务必配对使用
tryAcquire()和release(),推荐放在try-finally中 - 不要依赖多次连续调用
tryAcquire()的结果一致性 —— 每次都是独立快照 - 若需「确保获取成功或明确失败原因」,应结合超时重试或 fallback 逻辑,而非单次判断
带时间参数的 tryAcquire(long, TimeUnit) 怎么用才不踩坑?
这个重载方法看似支持“等一会儿”,但它和 acquireUninterruptibly() 或 acquire() 有本质区别:它仍是非阻塞语义的延伸——只在指定时间内自旋/挂起等待,一旦超时就放弃,不进入同步队列排队。也就是说,它不保证「只要有人释放,我就能等到」。
立即学习“Java免费学习笔记(深入)”;
典型误用:
if (sem.tryAcquire(100, TimeUnit.MILLISECONDS)) {
// 认为等了 100ms 肯定能拿到 → 错!
process();
}
实际中,这 100ms 可能全花在系统调度、GC 或锁竞争上,根本没轮到你检查许可数。
- 超时值建议 ≥ 10ms,太小(如 1ms)在高负载下几乎总失败,失去意义
- 该方法会响应中断,抛出
InterruptedException,必须处理,不能简单吞掉 - 它和无参版本一样,成功后必须对应
release(),否则许可泄露
为什么在并发量大时 tryAcquire() 失败率突然飙升?
这不是 Semaphore 的 bug,而是底层 CAS 操作在高争用下的自然表现。tryAcquire() 底层靠 compareAndSet 修改许可计数,当几十个线程同时冲同一个 state 字段时,绝大多数会失败——这是 ABA 问题之外更直接的“写冲突”。
现象包括:QPS 上去后,tryAcquire() 返回 false 的比例从 5% 暴涨到 70%,但监控显示许可长期有富余。
- 这不是许可不够,是“撞车”太狠。可考虑降低单次申请许可数(比如改用批量许可
tryAcquire(3)替代三次单次调用) - 避免在 tight loop 里反复调用
tryAcquire(),加微小随机退避(如Thread.sleep(new Random().nextInt(10)))能显著改善 - 若业务允许,用
fair = true构造Semaphore,虽性能略降,但失败更均匀,不易出现某线程持续饥饿
和 synchronized / ReentrantLock.tryLock() 对比时要注意什么?
Semaphore.tryAcquire() 和 ReentrantLock.tryLock() 表面相似,但语义不同:tryLock() 是对「同一把锁」的竞争,失败说明锁被别人持有着;而 tryAcquire() 是对「许可池总量」的竞争,失败只说明当前剩余许可 ≤ 0,不反映任何具体线程持有状态。
这意味着你无法通过 tryAcquire() 结果反推谁占用了资源,也无法像 getHoldCount() 那样查当前线程已占多少许可(Semaphore 本身不记录归属)。
- 别试图用
Semaphore实现可重入控制——它天生不支持,重复调用tryAcquire()会多扣许可 - 若需追踪许可使用方,得自己维护映射表(如
ConcurrentHashMap<thread integer></thread>),但会破坏轻量级设计初衷 - 在需要「独占+可重入+可诊断」场景,优先选
ReentrantLock;仅需「限流/信号量/资源池」时,Semaphore更合适
真正难的是权衡「立即失败」带来的控制力 vs 「虚假失败」引发的业务抖动。生产环境里,一次 tryAcquire() 返回 false 后要不要重试、重试几次、间隔多久,往往比怎么写调用本身更关键。


















