信号量(Semaphore)是 Java 多线程并发中的一种 JDK 内置同步器,通过它可以实现多线程对公共资源的并发访问控制。
信号量由来
限流器
信号量的主要应用场景是控制最多 N 个线程同时地访问资源,其中计数器的最大值即是许可的最大值 N。
现在我们需要开发一个限流器,同一时刻最多有10个请求可以执行。对于这样的需求,我们实现的方案有:
- 使用Atomic类
- 使用Lock
- 使用条件变量
- 使用信号量
使用Atomic类实现
java复制代码public class LimitByAtomic {
private static final AtomicInteger COUNTER = new AtomicInteger(10);
public void f() {
int count = COUNTER.decrementAndGet();
if (count < 0) {
COUNTER.incrementAndGet();
System.out.println("拒绝执行业务逻辑");
return; // 拒绝执行业务逻辑
}
try {
// 执行业务逻辑
System.out.println("执行业务逻辑");
} finally {
COUNTER.incrementAndGet();
}
}
}
使用Lock实现
java复制代码public class LimitByLock {
private int count = 10;
public void f() {
if (count <= 0) {
System.out.println("拒绝执行业务逻辑");
return;
}
synchronized (this) {
if (count <= 0) {
System.out.println("拒绝执行业务逻辑");
return;
}
count--;
}
try {
// 执行业务逻辑
System.out.println("执行业务逻辑");
} finally {
synchronized (this) {
count++;
}
}
}
}
使用条件变量实现
对于使用Atomic类还是Lock这两种实现方式,都有一个缺点,如果10个线程同时执行,当第11个线程来执行的时候,会被拒绝掉,这样就没有执行业务逻辑的机会,造成请求丢失。
所以我们可以通过线程等待-通知机制来解决上面的问题。如果10个线程同时执行,当第11个线程来执行的时候,先阻塞这第11个线程,等待前面的10个线程只要执行完一个,就通知第11个线程来执行。
java复制代码public class LimitByCondition {
private int count = 10;
public void f() throws Exception {
synchronized (this) {
while (count <= 0) {
System.out.pri