并发控制利器:Semaphore详解与应用
简介
Semaphore 是Java并发编程中的一个重要工具,用于管理对共享资源的访问权限,确保系统资源不会因过度访问而耗尽。形象地说,Semaphore 可以比喻为交通信号灯,它控制着能够同时进入特定区域(如马路)的车辆数(线程数)。当一定数量的车辆(线程)进入后,其余车辆必须等待,直到有车辆离开,空出“车位”(许可证)为止。在编程中,Semaphore 通过协调线程访问,保证公共资源的合理分配。
应用场景
Semaphore 特别适用于有限资源访问控制的场景,例如数据库连接池管理、文件读写控制等。一个典型示例是数据库连接限制:假设你需要从数万个文件中读取数据,并将其保存至数据库,虽然读取操作(IO密集型)可以并行处理,但数据库连接数有限(例如10个)。此时,Semaphore 可以用来控制仅有10个线程能同时获取数据库连接,避免超出连接池容量。
代码语言:javascript复制public class SemaphoreTest {
private static final int THREAD_COUNT = 30;
private static ExecutorService threadPool = Executors.newFixedThreadPool(THREAD_COUNT);
private static Semaphore s = new Semaphore(10);
public static void main(String[] args) {
for (int i = 0; i < THREAD_COUNT; i ) {
threadPool.execute(new Runnable() {
@Override
public void run() {
try {
s.acquire();
System.out.println("Saving data...");
s.release();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
});
}
threadPool.shutdown();
}
}
在这个例子中,尽管启用了30个线程,但最多只有10个线程能够同时执行数据库保存操作,通过 Semaphore(10)
初始化,确保了并发访问的线程数不超过10。
其他方法及实现机制
Semaphore 提供了多个方法来帮助管理资源访问:
- availablePermits():返回当前可用的许可证数量。
- getQueueLength():返回等待获取许可证的线程数。
- hasQueuedThreads():检查是否有线程正在等待许可证。
- reducePermits(int reduction):减少指定数量的许可证,是一个受保护方法。
- getQueuedThreads():返回所有等待的线程集合,同样是受保护方法。
Semaphore 的内部实现基于AQS(AbstractQueuedSynchronizer),利用了CLH队列来管理等待线程。CLH队列是一种FIFO(先进先出)的线程等待队列,当线程尝试获取许可证失败时,会被封装成节点加入到队列中等待。CLH队列的节点结构包含前驱节点和后继节点的引用,以及线程状态等信息,通过这些信息维护线程的等待顺序。
在尝试获取锁的操作中,AQS的acquire()
方法首先尝试快速获取资源,失败则通过addWaiter()
方法将当前线程封装成节点并加入队列。此过程体现了CLH队列的结构和等待机制,确保了线程安全且高效地获取和释放资源。
总之,Semaphore 作为一种灵活的并发控制工具,通过限制并发访问的数量,有效管理共享资源,是解决资源竞争和提高系统并发能力的重要手段。