Semaphore的基本概念
在Java中,Semaphore是位于java.util.concurrent包下的一个类。它的核心就是维护了一个许可集。简单来说,就是有一定数量的许可,线程需要先获取到许可,才能执行,执行完毕后再释放许可。
那么,这个许可是什么呢?其实,你可以把它想象成是对资源的访问权。比如,有5个许可,就意味着最多允许5个线程同时执行。线程可以通过acquire()方法来获取许可,如果没有可用的许可,该线程就会阻塞,直到有许可可用。
让我们看个简单的例子。假设咱们有一个限制了最多同时3个线程执行的Semaphore:
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
// 创建一个Semaphore实例,许可数量为3
private static final Semaphore semaphore = new Semaphore(3);
public static void main(String[] args) {
// 创建并启动三个线程
for (int i = 1; i <= 3; i++) {
new Thread(new Task(semaphore), "线程" + i).start();
}
}
static class Task implements Runnable {
private final Semaphore semaphore;
public Task(Semaphore semaphore) {
this.semaphore = semaphore;
}
@Override
public void run() {
try {
// 请求许可
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " 获取许可,正在执行");
Thread.sleep(1000); // 模拟任务执行
System.out.println(Thread.currentThread().getName() + " 执行完毕,释放许可");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
// 释放许可
semaphore.release();
}
}
}
}
线程运行时序图:
Semaphore的核心原理
Semaphore的核心是基于AQS(AbstractQueuedSynchronizer)这个框架。AQS是Java并发包中的一个非常重要的组件,它用来构建锁或者其他同步器。AQS提供了一种机制,可以让线程在访问某个资源前进入等待状态,并在资源可用时被唤醒。
也就是说,内置成员变量 Sync sync 是Semaphore的基础,默认是非公平的。
Semaphore维护了一个许可集,这个集合的大小在初始化时设定。每次调用acquire()方法,Semaphore会试图从这个集合中取出一个许可。如果没有可用的许可,线程就会被阻塞,直到有其他线程释放一个许可。相反,release()方法会增加许可的数量,并有可能唤醒等待的线程。
使用Semaphore的场景
咱们来聊聊Semaphore在实际编程中的应用场景。理解了Semaphore的基础和原理后,咱们现在可以探索它在实际场景中的具体使用。Semaphore非常灵活,可以用于多种场合,特别是在控制资源访问的并发环境中。
场景一:资源池
想象一下,小黑有一个数据库连接池,这个池子里只有几个数据库连接。如果所有的连接都被占用了,其他需要数据库连接的线程就得等待。这就是Semaphore的经典应用场景。通过限制可用的连接数量,Semaphore确保了不会有太多的线程同时访问数据库。
场景二:限流
在Web服务中,咱们可能想要限制某个服务的并发请求数量,以防止服务器过载。Semaphore可以很容易地实现这个功能。设置一个固定数量的许可,就可以限制同时处理的请求数量。