在Java中,Semaphore(信号量)是位于java.util.concurrent包中的一个同步工具类。它用于控制同时访问某一资源的线程数量,或者是同时执行某一任务的线程数量。信号量通常用来限制可以访问某些资源(物理或逻辑的)的线程数量。
核心机制
许可机制
Semaphore 维护一组虚拟许可,初始化时指定许可数量。线程通过 acquire() 尝试获取许可(支持阻塞或非阻塞方式),资源使用后通过 release() 释放许可。若许可数为 0,后续线程将阻塞直至有可用许可。
公平与非公平策略
支持公平模式(按请求顺序分配许可)和非公平模式(允许插队,默认策略),通过构造函数参数 fair 指定。
典型应用场景
资源访问限流
如数据库连接池、文件句柄池等场景,限制同时访问资源的线程数量。例如,仅允许 10 个线程同时使用数据库连接。
生产者-消费者模型
协调生产者和消费者的执行节奏,防止资源过载。例如,控制餐厅同时用餐的顾客数量。
多线程协作
实现线程间依赖关系的协调,例如要求多个前置任务完成后才能执行后续操作。
基本使用
核心方法
acquire():阻塞获取许可(支持中断)
tryAcquire():非阻塞尝试获取许可
release():释放许可
availablePermits():返回当前可用许可数
代码示例
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class SemaphoreDemo {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(5); // 初始化5个许可
ExecutorService executor = Executors.newCachedThreadPool();
for (int i = 0; i < 20; i++) {
executor.execute(() -> {
try {
semaphore.acquire(); // 获取许可
// 访问资源
System.out.println(Thread.currentThread().threadId());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
semaphore.release(); // 释放许可
}
});
}
executor.shutdown();
}
}
此示例限制最多 5 个线程同时执行资源访问。
注意事项
许可泄露
确保 release() 在 finally 块中执行,防止线程异常终止导致许可未被释放。
死锁风险
避免多个 Semaphore 嵌套使用时因获取顺序不一致引发死锁。
动态调整
可通过 drainPermits() 清空剩余许可,或通过构造函数动态调整总许可数(需同步控制)。
与 ReentrantLock 对比
跨线程释放:Semaphore 允许不同线程获取和释放许可,而 ReentrantLock 通常要求同一线程释放锁。
用途差异:Semaphore 用于控制资源并发数,ReentrantLock 用于保护临界区代码的原子性。