Java并发包(JUC)Semaphore详解
一、核心特性与定位
1.1 资源许可控制器
Semaphore是Java并发包中实现资源访问控制的核心工具类,通过维护一组虚拟许可(permits)来控制同时访问特定资源的线程数量。其核心设计理念基于计数器信号量模式,支持公平/非公平两种获取策略。
类结构定位:
1.2 核心特性矩阵
| 特性 | 行为表现 | 适用场景 |
|---|---|---|
| 许可数量控制 | 通过acquire()/release()增减许可 | 资源池、连接池管理 |
| 公平性选择 | 支持公平/非公平模式 | 响应时间敏感场景 |
| 线程安全 | 基于AQS实现 | 多线程环境下的精确控制 |
| 超时机制 | tryAcquire(timeout)支持 | 防止永久阻塞 |
二、核心机制解析
2.1 许可管理(AQS实现)
同步状态结构:
// 简化版AQS状态管理
private static final class Sync extends AbstractQueuedSynchronizer {
Sync(int permits) {
setState(permits); // 初始许可数
}
int getPermits() {
return getState();
}
// 尝试获取许可
protected int tryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 || compareAndSetState(available, remaining)) {
return remaining >= 0 ? 1 : -1;
}
}
}
// 释放许可
protected boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
int next = current + releases;
if (next < current) throw new Error("Maximum permit count exceeded");
if (compareAndSetState(current, next)) {
return true;
}
}
}
}
2.2 关键方法时序
2.3 中断与超时机制
中断处理:
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
三、典型使用场景
3.1 资源池管理
数据库连接池控制:
// 初始化10个许可
Semaphore connectionPermits = new Semaphore(10);
// 获取连接
public Connection getConnection() {
connectionPermits.acquire();
return physicalConnections.poll();
}
// 释放连接
public void releaseConnection(Connection conn) {
physicalConnections.offer(conn);
connectionPermits.release();
}
3.2 速率限制
API调用限流:
// 每秒100个许可
Semaphore rateLimiter = new Semaphore(100);
// 初始化时创建定时任务补充许可
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() -> {
rateLimiter.release(100 - rateLimiter.availablePermits());
}, 1, 1, TimeUnit.SECONDS);
// 业务方法
public Response handleRequest(Request req) {
rateLimiter.acquire();
return process(req);
}
3.3 任务调度
并发任务控制:
// 最大并发数5
Semaphore taskSemaphore = new Semaphore(5);
// 任务提交
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
final int taskId = i;
executor.submit(() -> {
taskSemaphore.acquire();
try {
executeTask(taskId);
} finally {
taskSemaphore.release();
}
});
}
四、最佳实践
4.1 许可数量初始化
动态配置策略:
// 根据配置动态初始化
int maxConnections = Integer.parseInt(System.getProperty("db.pool.size", "10"));
Semaphore connectionSemaphore = new Semaphore(maxConnections);
避免魔法数字:
// 使用常量定义
private static final int MAX_API_CALLS = 1000;
Semaphore apiQuota = new Semaphore(MAX_API_CALLS);
4.2 超时控制模式
分级超时机制:
// 主超时控制
boolean acquired = false;
try {
if (semaphore.tryAcquire(30, TimeUnit.SECONDS)) {
acquired = true;
processResource();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
if (acquired) {
semaphore.release();
} else {
handleAcquisitionFailure();
}
}
4.3 资源清理模式
优雅关闭实现:
// 资源使用带清理
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
try {
semaphore.acquire();
useResource();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
semaphore.release();
cleanupResource(); // 确保资源释放
}
}).start();
}
五、常见问题与解决方案
5.1 许可泄漏问题
现象:
- 许可数持续减少
- 线程阻塞在acquire()
解决方案:
// 使用try-with-resources模式
try (AutoCloseableResource resource = acquireResource()) {
use(resource);
} catch (Exception e) {
handleException(e);
}
// 自定义资源包装类
class AutoCloseableResource implements AutoCloseable {
private final Semaphore semaphore;
public AutoCloseableResource(Semaphore sem) {
this.semaphore = sem;
sem.acquire();
}
@Override
public void close() {
semaphore.release();
}
}
5.2 线程中断处理
最佳实践:
// 正确处理中断
try {
semaphore.acquire();
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 恢复中断状态
// 执行清理操作
handleInterrupt();
}
5.3 性能瓶颈优化
诊断方法:
- 使用JVisualVM监控线程状态
- 检测长时间acquire()的线程
- 分析许可释放频率
优化技巧:
// 批量release优化
for (int i = 0; i < BATCH_SIZE; i++) {
processItem();
}
semaphore.release(BATCH_SIZE); // 批量释放许可
六、总结
Semaphore 通过 AQS 的共享锁机制,以高效的 CAS 操作和队列管理实现并发控制。其源码核心在于:
- 许可计数器:通过
state变量维护剩余许可数。 - 公平性策略:通过
hasQueuedPredecessors()判断队列状态。 - 阻塞唤醒:依赖 AQS 的
Condition机制管理等待线程。
理解其源码有助于在高并发场景中灵活控制资源访问,避免过载和竞争问题。
Java并发包Semaphore详解
5212

被折叠的 条评论
为什么被折叠?



