semaphore本身不保障共享资源线程安全,仅作准入控制:通过许可计数器限制并发线程数,防止资源耗尽;许可数为1时模拟互斥,大于1时允许多线程进入临界区,此时需配合synchronized、lock等机制确保数据一致性,且acquire/release必须成对出现在try-finally中以防许可泄漏。

Semaphore 本身不直接保障共享资源的线程安全,但它在并发场景中承担着不可替代的“准入控制”角色——它不解决数据竞争(比如多个线程同时改一个变量),而是防止因并发量失控导致的系统过载、资源耗尽或服务质量崩塌。
它解决的是“太多人抢有限资源”的问题
比如数据库连接池只有 5 个连接,却有 20 个线程争抢;又比如磁盘写入能力最多支撑 4 路并发,但业务请求涌来上百路。这时,Semaphore 就是那个守门人:只放行指定数量的线程进入临界区,其余排队等待。
- 它通过许可(permits)计数器实现硬性上限,acquire() 成功才允许执行,否则阻塞
- 许可数设为 1 时,可模拟互斥效果;设为 N 时,就是 N 路并发闸门
- 它不干涉临界区内代码逻辑,所以必须配合 synchronized、ReentrantLock 或原子类等,才能真正保障数据一致性
必须成对调用 acquire 和 release
这是最容易出错也最致命的一环:漏掉 release() 或异常未进 finally 块,会导致许可永久丢失,可用数持续减少,最终所有线程卡在 acquire() 上,系统逐步僵死。
- 所有 acquire() 调用都应包裹在 try-finally 中,确保 release() 无条件执行
- release() 不校验调用者是否曾 acquire() 过,只是单纯加许可数,所以误调多次也不会报错,但会破坏配额逻辑
- acquire(int n) 和 release(int n) 支持批量操作,但要求 n 必须一致且原子性扣减,否则许可状态会错乱
公平性影响调度行为,但不改变本质功能
构造时传入 true 启用公平模式,会让等待最久的线程优先获取许可,避免饥饿;非公平模式性能略高,但可能让新线程“插队”。无论哪种,都只控制谁先拿到号,不改变“总共只发 N 个号”的事实。
- 公平性解决的是等待策略问题,不是资源安全问题
- 高吞吐场景(如短任务+频繁进出)通常用非公平;长等待+强顺序要求场景可考虑公平
- 公平模式不能替代锁的语义,也不能防止数据竞争,仅优化排队秩序
它和线程安全工具是协作关系,不是替代关系
举个典型例子:用 Semaphore 控制 3 个线程并发写文件,但每个线程内部仍需保证写操作的原子性(比如用 FileOutputStream + 同步块,或使用 FileChannel.write() 配合 position 锁)。Semaphore 管“能不能进”,其他机制管“进了之后怎么不打架”。
- 单独用 Semaphore ≠ 线程安全;单独用 synchronized ≠ 流量可控
- 真实项目中常组合使用:Semaphore 限并发总数 + ReentrantLock 保临界区操作原子性 + volatile/AtomicInteger 做状态标记
- 监控 availablePermits() 可辅助诊断许可泄漏或配置不合理问题
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











