Java并发-信号量Semaphore
在高并发的场景下,我们经常需要控制同时访问某个资源的线程数量。比如说,一个数据库连接池只能支持20个并发连接,或者一个第三方API每秒只能处理100个请求。这时候就需要用到信号量(Semaphore
)这个强大的并发控制工具。
想象一下这样的场景:你开了一家火锅店,店里只有10张桌子。顾客来了要先拿号排队,有桌子空出来才能进去吃饭,吃完后要把桌子让出来给下一位客人。Semaphore
就像是这个排队系统,控制着同时能够使用资源的线程数量。
# 一、基础用法
先来看一个简单的例子:
import java.util.concurrent.*;
public class SemaphoreDemo {
// 创建一个许可证数量为5的信号量
private final Semaphore semaphore = new Semaphore(5);
public void doWork(int workerId) {
try {
// 获取许可证
semaphore.acquire();
System.out.println("工作者 " + workerId + " 开始工作,当前可用许可证:" + semaphore.availablePermits());
// 模拟工作时间
Thread.sleep(2000);
System.out.println("工作者 " + workerId + " 工作完成");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
// 释放许可证
semaphore.release();
System.out.println("工作者 " + workerId + " 释放许可证");
}
}
public static void main(String[] args) {
SemaphoreDemo demo = new SemaphoreDemo();
ExecutorService executor = Executors.newFixedThreadPool(10);
// 启动10个任务,但同时只能有5个执行
for (int i = 1; i <= 10; i++) {
final int workerId = i;
executor.submit(() -> demo.doWork(workerId));
}
executor.shutdown();
}
}
运行这个程序,你会发现虽然提交了10个任务,但同时执行的永远不会超过5个。
# 二、核心方法详解
Semaphore
提供了多个获取和释放许可证的方法:
# 1、获取许可证
acquire()
- 获取一个许可证,如果没有可用许可证则阻塞等待acquire(int permits)
- 获取指定数量的许可证acquireUninterruptibly()
- 获取许可证,不响应中断tryAcquire()
- 尝试获取许可证,立即返回结果,不等待tryAcquire(long timeout, TimeUnit unit)
- 在指定时间内尝试获取许可证
# 2、释放许可证
release()
- 释放一个许可证release(int permits)
- 释放指定数量的许可证
# 3、状态查询
availablePermits()
- 返回当前可用的许可证数量getQueueLength()
- 返回正在等待获取许可证的线程数量hasQueuedThreads()
- 是否有线程在等待
# 三、实际应用场景
# 1、限流控制
public class ApiRateLimiter {
// 每秒最多处理100个请求
private final Semaphore semaphore = new Semaphore(100);
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
public ApiRateLimiter() {
// 每秒恢复所有许可证
scheduler.scheduleAtFixedRate(() -> {
int used = 100 - semaphore.availablePermits();
if (used > 0) {
semaphore.release(used);
}
}, 1, 1, TimeUnit.SECONDS);
}
public boolean tryProcessRequest() {
return semaphore.tryAcquire();
}
}
# 2、连接池管理
public class DatabaseConnectionPool {
private final Semaphore connectionSemaphore;
private final Queue<Connection> availableConnections;
public DatabaseConnectionPool(int maxConnections) {
this.connectionSemaphore = new Semaphore(maxConnections);
this.availableConnections = new ConcurrentLinkedQueue<>();
// 初始化连接
for (int i = 0; i < maxConnections; i++) {
availableConnections.offer(createConnection());
}
}
public Connection getConnection() throws InterruptedException {
connectionSemaphore.acquire();
return availableConnections.poll();
}
public void returnConnection(Connection conn) {
availableConnections.offer(conn);
connectionSemaphore.release();
}
private Connection createConnection() {
// 创建数据库连接的逻辑
return null;
}
}
# 四、高级特性
# 1、公平性
Semaphore
支持公平模式,确保等待时间最长的线程优先获得许可证:
// 公平模式的信号量
Semaphore fairSemaphore = new Semaphore(5, true);
# 2、的灵活用法
public class FlexibleResourceAccess {
private final Semaphore semaphore = new Semaphore(3);
public void quickTask() {
// 立即尝试获取,不等待
if (semaphore.tryAcquire()) {
try {
System.out.println("执行快速任务");
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
semaphore.release();
}
} else {
System.out.println("资源繁忙,跳过任务");
}
}
public void timeoutTask() {
try {
// 最多等待5秒
if (semaphore.tryAcquire(5, TimeUnit.SECONDS)) {
try {
System.out.println("执行超时任务");
Thread.sleep(2000);
} finally {
semaphore.release();
}
} else {
System.out.println("等待超时,放弃任务");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
# 五、使用注意事项
# 1、异常安全
永远在 finally
块中释放许可证,确保即使发生异常也能正确释放:
semaphore.acquire();
try {
// 业务逻辑
} finally {
semaphore.release(); // 确保释放
}
# 2、避免许可证泄漏
每次 acquire()
都要对应一个 release()
,否则会导致许可证永久丢失。
# 3、中断处理
acquire()
方法会响应中断,要正确处理 InterruptedException
:
try {
semaphore.acquire();
// 业务逻辑
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 恢复中断状态
return;
} finally {
semaphore.release();
}
# 六、与其他并发工具的对比
- CountDownLatch:一次性使用,等待多个线程完成
- CyclicBarrier:可重复使用,等待多个线程到达同步点
- Semaphore:控制同时访问资源的线程数量,可重复使用
Semaphore
更适合资源池管理和流量控制场景,而 CountDownLatch
和 CyclicBarrier
更适合线程间的协调同步。
# 七、小结
Semaphore
是 Java 并发编程中的重要工具,它通过许可证机制优雅地解决了资源访问控制问题。无论是限流、连接池管理,还是其他需要控制并发数的场景,Semaphore
都能提供简洁而高效的解决方案。
关键是要理解它的核心思想:通过许可证来控制同时访问共享资源的线程数量。掌握了这个思想,你就能在各种场景下灵活运用这个强大的并发工具了。
编辑 (opens new window)
上次更新: 2025/08/14