Semaphore源码阅读
本人的源码阅读主要聚焦于类的使用场景,一般只在java层面进行分析,没有深入到一些native方法的实现。并且由于知识储备不完整,很可能出现疏漏甚至是谬误,欢迎指出共同学习
本文基于corretto-17.0.9源码,参考本文时请打开相应的源码对照,否则你会不知道我在说什么
简介
semaphore翻译过来即信号量,对semaphore最直观的解释就是,信号量是一个令牌桶,如果桶里的令牌数>=线程想要获取的令牌数,那么线程就能获取到令牌,然后继续执行,否则就要阻塞等待其他线程将令牌放入桶里,直到桶里令牌数足够,非常简单。
不像JUC里的Lock,线程只能释放自己获得的锁,即Lock有owner的概念,不能随便释放其他人的锁。而semaphore中令牌没有owner的概念,任何线程都能随意往桶里放令牌。
代码分析
成员变量
public class Semaphore implements java.io.Serializable {
private final Sync sync;
}
成员变量非常简单只有一个,看到Sync就该知道这个是AQS了。信号量还支持fairness,因此Sync作为抽象基类,实现了NonFairSync和FairSync。
方法
Sync
Sync继承自AQS,state定义为桶中令牌数量。由于可能同时有多个线程获得令牌,自然需要重写AQS的两个方法:
- tryAcquireShared
- tryReleaseShared
在tryAcquire这里需要区分公平与否,在之前的文章说过,公平的tryAcquire通过检查是否有前驱等待者,即下面两个条件满足一个都属于有前驱等待者,那么就直接tryAcquire失败:
- 当前线程没进入阻塞队列,且队列不为空
- 当前线程已经在阻塞队列中,但前面有更靠近队头(等待时间更长)的线程
否则如果是非公平的,就直接尝试更改state即可。
abstract static class Sync extends AbstractQueuedSynchronizer {
// 初始化state为令牌数量
Sync(int permits) {
setState(permits);
}
final int getPermits() {
return getState();
}
// nonfair版本的tryAcquireShared,如果目前令牌足够,直接更改令牌数(state)即可
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
// 往桶里放令牌,直接更改state
protected final boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
int next = current + releases;
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
if (compareAndSetState(current, next))
return true;
}
}
// 忽略其他非核心方法
}
NonFairSync
static final class NonfairSync extends Sync {
NonfairSync(int permits) {
super(permits);
}
// 非公平tryAcquire
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
}
FairSync
static final class FairSync extends Sync {
private static final long serialVersionUID = 2014338818796000944L;
FairSync(int permits) {
super(permits);
}
// 公平tryAcquire
protected int tryAcquireShared(int acquires) {
for (;;) {
// 检查是否有阻塞更久的线程
if (hasQueuedPredecessors())
return -1;
int available = getState();
int remaining = available - acquires;
if (remaining < 0 || compareAndSetState(available, remaining))
return remaining;
}
}
}