semaphore的核心作用是配额管理而非加锁,通过许可数量控制并发线程数;其计数器为动态配额池,支持批量申请、跨线程释放、公平/非公平调度,需在finally中释放以防泄漏,适用于有明确并发上限的场景。

Semaphore信号量的核心作用不是“加锁”,而是“配额管理”——它不保证线程独占资源,而是控制有多少线程能同时进入临界区。理解这一点,才能避开把它当ReentrantLock用的常见误区。
许可证机制:本质是动态配额池
Semaphore内部维护一个整型计数器(即许可数量),每次acquire()成功就减1,release()就加1。这个计数器不是状态标记,而是实时可用额度:
- 初始值为N时,最多N个线程可并发执行,第N+1个线程会阻塞等待
- acquire(2)可一次申请2个许可,适合批量资源占用场景(如一次读取2个文件句柄)
- availablePermits()返回当前剩余许可数,可用于监控或动态限流决策
- 许可可跨线程释放:线程A acquire 后,线程B可调用 release,只要逻辑合理(如异步回调归还)
公平性选择:影响调度行为而非安全性
构造时传入true启用公平模式,意味着等待队列按FIFO顺序唤醒;默认非公平模式可能产生“插队”,但吞吐更高:
- 公平模式适合对响应时间敏感、需避免饥饿的场景(如金融交易请求排队)
- 非公平模式更适用于高吞吐任务(如日志批量写入),允许刚释放许可的线程快速重入
- 公平性不影响线程安全——无论哪种模式,acquire/release操作本身都是原子且线程安全的
异常与中断:必须在finally中释放
acquire()可能抛出InterruptedException,若未正确释放许可,会导致许可证永久泄漏,最终所有线程阻塞:
- 务必把release()放在finally块中,确保无论正常结束还是异常退出都归还许可
- 捕获InterruptedException后应立即恢复中断状态:Thread.currentThread().interrupt()
- 优先使用tryAcquire(timeout, unit)替代无参acquire(),避免无限等待导致服务雪崩
典型实战边界:别越界,也别低估
信号量不是万能同步方案,适用有明确“并发数上限”的场景,不适用于需要互斥写入或状态依赖的逻辑:
- 适合:数据库连接池限流、爬虫并发控制、打印机任务队列、API调用频次限制
- 不适合:共享变量累加(仍需AtomicInteger或synchronized)、多步骤事务一致性校验
- 注意许可数设置:过小造成大量线程阻塞,上下文切换开销上升;过大则失去限流意义
- 二进制信号量(new Semaphore(1))功能接近锁,但不支持重入和条件等待,慎用于复杂临界区











