semaphore的核心作用是控制并发访问数量:acquire阻塞等待许可,需异常处理且推荐finally释放;tryacquire非阻塞试探,返回boolean,适合高响应性场景;释放许可必须在finally中确保执行。

Semaphore 的核心作用是控制并发访问数量,而 tryAcquire 和 acquire 是获取许可的两种关键方式:前者非阻塞、立即返回结果;后者会阻塞等待,直到获得许可或被中断。
acquire:需要等待的许可获取
调用 acquire() 时,如果当前没有可用许可,线程会进入阻塞状态,并加入等待队列,直到其他线程调用 release() 释放许可,或者当前线程被中断(抛出 InterruptedException)。它适合对资源访问有强保障要求的场景。
- 必须配合 try-catch 或声明 throws InterruptedException 使用
- 推荐在 finally 块中调用 release(),防止因异常导致许可泄漏
- 支持重载形式 acquire(int permits),可一次性申请多个许可
- 若构造时指定 fair = true,等待线程按 FIFO 顺序获取许可
tryAcquire:不等待的许可试探
tryAcquire() 立即返回 boolean 值:true 表示成功拿到许可,false 表示当前无可用许可,且不会阻塞。它适合对响应性要求高、能接受“暂时不可用”的业务逻辑。
- 常用在限流降级、快速失败等场景,比如 API 请求超出配额时直接返回错误
- 有带超时的重载 tryAcquire(long timeout, TimeUnit unit),在指定时间内轮询等待
- 也支持多许可申请:tryAcquire(int permits) 和 tryAcquire(int permits, long timeout, TimeUnit unit)
- 不抛异常,无需强制异常处理,代码更简洁
如何选:阻塞 vs 非阻塞取决于业务语义
是否允许线程停住等待,本质是业务容忍度问题:
- 打印任务、数据库连接池分配等——资源必须拿到才能继续,用 acquire()
- 秒杀抢购、接口熔断、后台异步任务调度等——拿不到就跳过或重试,用 tryAcquire()
- 想兼顾等待与超时控制?选 tryAcquire(timeout, unit),避免无限等待
- 注意:acquireUninterruptibly() 是 acquire() 的不可中断版本,适用于不能被中断的关键路径
release 的正确姿势不能少
无论用哪种方式获取许可,释放都必须可靠执行。最稳妥写法是放在 finally 块中:
- 即使 acquire() 成功后发生异常,也能确保许可归还
- 不要在 if (tryAcquire()) { ... } 分支外漏掉 release()
- release() 可以由任意线程调用,不要假设“谁 acquire 就必须谁 release”
- 多次 release 会导致许可数超过初始值,可能破坏限流逻辑,需谨慎校验











