信号量(Semaphore)是 Java java.util.concurrent
包中的一种同步辅助类,用于控制对共享资源的访问。在并发编程中,信号量常用于限制同时访问特定资源的线程数量,避免过多线程同时访问可能导致的资源竞争或性能下降。
使用场景
- 限制并发访问:当某个资源(如连接池、数据库等)有限制时,可以使用信号量来控制同时访问该资源的线程数量。
- 节流控制:限制请求的处理速率,以防止系统过载。
- 多线程协调:在某些情况下,信号量可以用于在多个线程之间进行协调。
基本使用
以下是一个使用信号量的基本示例:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
public static void main(String[] args) {
// 创建信号量,允许最多3个线程同时访问
final Semaphore semaphore = new Semaphore(3);
// 创建线程池
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
final int threadNumber = i;
executor.submit(() -> {
try {
// 获取信号量
semaphore.acquire();
System.out.println("线程 " + threadNumber + " 获得信号量,正在执行...");
// 模拟执行任务
Thread.sleep(2000);
System.out.println("线程 " + threadNumber + " 执行完毕,释放信号量。");
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 恢复中断状态
} finally {
// 释放信号量
semaphore.release();
}
});
}
executor.shutdown();
}
}
代码分析
- 创建信号量:使用
new Semaphore(3)
创建一个信号量,允许最多 3 个线程同时访问。 - 提交任务:使用
ExecutorService
创建线程池并提交 10 个任务。 - 获取信号量:在每个线程中,调用
semaphore.acquire()
尝试获取信号量。如果当前信号量的许可数量为 0,线程会被阻塞,直到其他线程释放信号量。 - 执行任务:线程获得信号量后,模拟执行某些操作(如休眠 2 秒)。
- 释放信号量:执行完成后,无论成功与否,确保在
finally
块中调用semaphore.release()
,以释放信号量,让其他等待线程可以继续执行。
注意事项
- 信号量许可证的数量:信号量的许可证数量应该基于实际需要进行设置,以避免过多的线程同时获取许可造成系统资源耗尽。
- 捕获 InterruptedException:使用
acquire()
方法时,可能会抛出InterruptedException
,应当妥善处理。 - 公平性:
Semaphore
有一个构造函数可以接受一个布尔参数,指定是否采用公平策略。公平策略保证先请求的线程先获得许可,但可能会影响性能。
Semaphore semaphore = new Semaphore(3, true); // 公平模式
通过合理使用信号量,可以有效地管理多线程对共享资源的访问,避免资源竞争和潜在的性能问题。