Java并发编程进阶:Semaphore 原理与源码解析
📖 Java 并发编程专栏持续更新中
从 JMM → volatile → CAS → synchronized → AQS → ReentrantLock → Semaphore,逐步深入 Java 并发底层原理。
更多原创技术文章可在公众号历史消息中查看,欢迎关注交流。🚀
文章目录
一、什么是 Semaphore

Semaphore(信号量)是 JDK 提供的一个并发工具类,用来控制同时访问某个资源的线程数量
位于:java.util.concurrent.Semaphore
例如:Semaphore semaphore = new Semaphore(3);
表示:
当前资源有3个许可证(Permit)
线程1 获取许可证
线程2 获取许可证
线程3 获取许可证此时许可证耗尽
线程4必须等待
当某个线程释放许可证:
semaphore.release();
线程4才能继续执行
二、应用场景
场景1:数据库连接池
假设连接池:最大连接数 = 30
那么:
Semaphore semaphore = new Semaphore(30);获取连接:semaphore.acquire();
归还连接:semaphore.release();
保证:最多30个线程同时访问数据库
场景2、限流(限制并发量)
限制接口并发数:最多100个请求同时处理
Semaphore semaphore =
new Semaphore(100);
场景3、爬虫并发控制
Semaphore semaphore =
new Semaphore(10);
同时最多10个线程抓取网页
避免:把目标网站打挂
简单示例:允许3个线程同时执行
public class SemaphoreDemo {
private static final Semaphore semaphore =
new Semaphore(3);
public static void main(String[] args) {
for(int i=1;i<=10;i++){
int num=i;
new Thread(() -> {
try {
semaphore.acquire();
System.out.println(
num + " 获取许可证");
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
} finally {
semaphore.release();
System.out.println(
num + " 释放许可证");
}
}).start();
}
}
}
输出类似:
1 获取许可证
2 获取许可证
3 获取许可证(其余线程等待)
1 释放许可证
4 获取许可证
说明:
最多只有3个线程同时执行
三、Semaphore核心API
在说核心API前先说一下下面频繁出现的permit是什么
permit是什么
通过前面学习 ReentrantLock 我们知道,AQS 内部维护了一个 state 变量
在 ReentrantLock 中:
state = 0 表示未加锁
state = 1 表示已加锁
state > 1 表示锁重入次数
那么 Semaphore 中经常提到的 permit(许可证) 又是什么呢
查看 Semaphore 源码:

Sync(int permits) {
setState(permits);
}
继续查看 setState() 方法,会发现它来自 AQS:

protected final void setState(int newState) {
state = newState;
}
因此可以发现,Semaphore 并没有单独维护一个 permit 变量
当我们创建:
Semaphore semaphore = new Semaphore(3);
时,实际上执行的是:
state = 3;
这里的
3就表示当前可用的许可证数量
在 Semaphore 中:
state = 剩余可用许可证数量(Permit Count)
例如:
state = 3 剩余3个许可证
state = 2 剩余2个许可证
state = 1 剩余1个许可证
state = 0 没有可用许可证
每当线程执行:
semaphore.acquire();本质上就是尝试将:state--
而执行:
semaphore.release();本质上就是:state++
因此可以理解为:
Permit(许可证)
↓
由AQS中的state维护ReentrantLock:
state = 锁状态/重入次数Semaphore:
state = 剩余许可证数量
acquire()
获取许可证
semaphore.acquire();
流程:
有许可证
↓
permit--
↓
继续执行没有许可证
↓
进入AQS队列
↓
阻塞等待
release()
释放许可证
semaphore.release();
流程:
permit++
↓
唤醒等待线程
tryAcquire()
尝试获取许可证
if(semaphore.tryAcquire()){
// 获取成功
}
不会阻塞
成功 → true
失败 → false
acquire(int n)
一次获取多个许可证
semaphore.acquire(3);
表示:
必须一次拿到3个Permit
否则等待
availablePermits()
查看剩余许可证
int count = semaphore.availablePermits();
四、Semaphore内部原理
实际上和 ReentrantLock 非常像:
Semaphore = AQS + state
AQS中的state
在 ReentrantLock 中:
state = 持有锁次数
例如:
state = 0 未加锁
state = 1 已加锁
state = 2 重入两次
Semaphore 中:
state = 剩余许可证数量
例如:
new Semaphore(3);表示初始化state = 3
为什么使用共享模式
AQS有两种模式:
独占模式(Exclusive)
共享模式(Shared)
ReentrantLock:
独占模式
因为:同一时刻只能一个线程持有锁
Semaphore:
共享模式
因为:同一时刻允许多个线程成功获取资源
例如:
new Semaphore(5);可以同时:
线程A成功 线程B成功 线程C成功 线程D成功 线程E成功所以必须使用:AQS Shared Mode
五、获取许可证过程
假设:state = 3
线程A执行:acquire()
AQS内部:
具体源码见:Semaphore.java文件
链路:
acquire()
↓
acquireSharedInterruptibly(1)
↓
Sync.tryAcquireShared()
↓
NonfairSync 或 FairSync
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
结果:
state: 3 → 2
成功获得许可证
线程B:state: 2 → 1
线程C:state: 1 → 0
线程D:state = 0
再执行:acquire()
发现:remaining < 0 获取失败
进入:AQS同步队列
然后:LockSupport.park(); 挂起
六、释放许可证过程
线程A执行:
release();
内部:
protected final boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
int next = current + releases;
if (compareAndSetState(current, next))
return true;
}
}
例如:state : 0 → 1
然后:doReleaseShared(); 唤醒等待线程
七、公平与非公平模式
非公平(默认)
new Semaphore(5);
等价于:
new Semaphore(5,false);
谁抢到谁先执行
吞吐量最高
公平模式
new Semaphore(5,true);
严格FIFO
队列:
A
B
C
D
释放许可证后,必须A先获取,不能插队
八、与ReentrantLock区别
| 对比项 | ReentrantLock | Semaphore |
|---|---|---|
| 本质 | 锁 | 信号量 |
| AQS模式 | 独占 | 共享 |
| 同时允许线程数 | 1 | N |
| state含义 | 重入次数 | 剩余许可证 |
| 应用场景 | 临界区保护 | 流量控制 |
九、总结
Semaphore 本身代码并不复杂,核心思想只有一句话:
把AQS中的state当作许可证数量使用
获取许可证:
CAS(state--)
成功 -> 执行失败 -> 进入AQS队列等待
释放许可证:
CAS(state++)
成功 -> 唤醒等待线程
因此从源码角度看:
ReentrantLock:
state = 锁重入次数Semaphore:
state = 剩余许可证数量
更多推荐
所有评论(0)