Semaphore通过许可证机制限制并发线程数,初始化时设定许可数量,acquire()获取许可,release()释放许可,确保最多n个线程同时执行,适用于资源限流与连接池管理。

在Java多线程编程中,控制并发访问资源的线程数量是一个常见需求。Semaphore(信号量)是java.util.concurrent包中的一个同步工具类,能够有效限制同时访问特定资源的线程数量。它通过维护一组许可证(permits)来实现这一功能:线程在执行前需要先获取许可,执行完成后释放许可,若没有可用许可,线程将被阻塞直到有其他线程释放。
理解Semaphore的基本机制
Semaphore初始化时指定许可数量,代表最多允许多少个线程同时访问临界区。其核心方法包括:
- acquire():线程尝试获取一个许可,如果没有可用许可,则阻塞等待。
- acquire(int permits):一次性获取多个许可。
- release():释放一个许可,将其归还给信号量。
- release(int permits):释放多个许可。
- tryAcquire():非阻塞方式尝试获取许可,立即返回true或false。
使用公平模式(fair = true)可确保等待最久的线程优先获得许可,避免线程饥饿。
限制并发线程数的实际应用示例
假设我们有一个服务需要处理大量任务,但数据库连接池最多支持5个并发连接。我们可以用Semaphore来限制同时运行的任务数量。
立即学习“Java免费学习笔记(深入)”;
import java.util.concurrent.Semaphore; public class TaskExecutor { // 定义最多5个并发许可 private final Semaphore semaphore = new Semaphore(5); public void executeTask(Runnable task) { Thread thread = new Thread(() -> { try { // 获取许可,可能阻塞 semaphore.acquire(); System.out.println(Thread.currentThread().getName() + " 开始执行任务"); // 模拟任务执行 Thread.sleep(2000); task.run(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { System.out.println(Thread.currentThread().getName() + " 任务完成,释放许可"); // 释放许可 semaphore.release(); } }); thread.start(); } public static void main(String[] args) { TaskExecutor executor = new TaskExecutor(); // 提交10个任务 for (int i = 0; i < 10; i++) { executor.executeTask(() -> System.out.println("任务执行中...")); } } }
在这个例子中,尽管创建了10个线程,但只有5个能同时运行,其余线程会等待前面的线程释放许可后才开始执行。
结合tryAcquire实现超时与降级策略
在高并发场景下,长时间阻塞可能影响系统响应。可以使用tryAcquire配合超时机制,提升系统的健壮性。
boolean acquired = semaphore.tryAcquire(3, TimeUnit.SECONDS); if (acquired) { try { // 执行业务逻辑 } finally { semaphore.release(); } } else { // 超时未获取到许可,执行降级逻辑,如返回缓存数据或提示繁忙 System.out.println("请求被拒绝,系统繁忙"); }
这种方式适用于对响应时间敏感的服务,比如接口限流或防止雪崩。
注意事项与最佳实践
Semaphore虽然强大,但使用时需要注意以下几点:
- 确保每次acquire()后都有对应的release(),建议放在finally块中,防止死锁或资源泄露。
- 合理设置许可数量,过小会限制吞吐量,过大则失去限流意义。
- 不要将Semaphore用于替代锁(如ReentrantLock),它不保证互斥访问,只控制并发数量。
- 在异步回调或多阶段任务中,要特别注意许可释放的时机,避免提前释放。
基本上就这些。Semaphore是一种轻量且灵活的并发控制工具,适合用于资源限流、连接池管理、任务节流等场景。只要掌握好获取与释放的配对逻辑,就能在实际项目中稳定发挥其作用。


