Semaphore用于控制同时访问共享资源的线程数量,通过许可(permits)机制实现:acquire()获取许可,release()归还许可;构造时指定总数,支持公平/非公平模式,必须成对调用以防泄漏。

在 Java 中,Semaphore 是一种用于控制**同时访问某一共享资源的线程数量**的同步工具。它特别适合限制对有限物理资源(如数据库连接、文件句柄、硬件设备、API 调用配额等)的并发访问,避免资源过载或争用失败。
理解 Semaphore 的核心概念
Semaphore 维护一组“许可”(permits)。每个线程在访问受保护资源前需调用 acquire() 获取一个许可;使用完毕后必须调用 release() 归还许可。若当前无可用许可,acquire() 会阻塞,直到其他线程释放许可。
关键点:
- 构造时指定许可总数(即最大并发数),例如
new Semaphore(3)表示最多 3 个线程可同时通行; - 支持公平/非公平模式(默认非公平),公平模式下按等待顺序分配许可;
- 许可不是绑定到特定线程的——谁 acquire 谁 release,但必须成对出现,否则会导致许可泄漏或死锁。
典型使用步骤(以控制打印机访问为例)
假设系统只有一台高速打印机,但最多允许 2 个线程同时提交打印任务(防止队列阻塞或硬件过热):
立即学习“Java免费学习笔记(深入)”;
1. 创建全局共享的 Semaphore 实例(通常为 static final):
private static final Semaphore printerSemaphore = new Semaphore(2);
2. 在访问物理资源前获取许可(建议使用 try-finally 确保释放):
try {
printerSemaphore.acquire(); // 阻塞直到获得许可
// 执行实际打印操作(如调用 native 打印接口、写入 /dev/lp0 等)
doPhysicalPrint(document);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
printerSemaphore.release(); // 必须释放,否则许可数永久减少
}
处理超时与中断,提升健壮性
生产环境中不建议无限期阻塞。可使用带超时的 acquire(long timeout, TimeUnit unit) 避免线程长期挂起:
- 返回
true表示成功获取许可; - 返回
false表示超时未获取到,应记录日志并降级处理(如排队、拒绝、重试); - 若线程被中断,会抛出
InterruptedException,需正确响应中断状态。
示例:
if (!printerSemaphore.tryAcquire(3, TimeUnit.SECONDS)) {
log.warn("打印许可获取超时,文档 {} 将被丢弃", docId);
throw new ResourceBusyException("Printer is busy");
}
try {
doPhysicalPrint(document);
} finally {
printerSemaphore.release();
}
注意事项与常见陷阱
⚠️ 这些细节直接影响信号量是否真正生效:
-
必须成对调用 acquire/release:遗漏
release()会导致许可永久丢失,后续线程全部阻塞; - 不要在 synchronized 块内仅靠 Semaphore 控制资源:二者语义不同,Semaphore 控制的是逻辑并发数,不替代临界区互斥;
-
资源初始化失败时也要 release:若
doPhysicalPrint()抛异常且已占用硬件通道,需在 finally 中清理通道并 release; - 避免在 acquire 前做耗时操作:否则许可空闲等待期间浪费,降低吞吐;
- 若需动态调整并发数,可用
reducePermits()或release(int n),但需确保线程安全。


















