【源码】JUC —— Semaphore 浅析
前言
Semaphore ,翻译:信号量,跟 ReentrantLock 相对的,共享锁 的经典实现,基于 AQS ,也提供了 公平锁 和 非公平锁 两种机制
关于 ReentrantLock ,可阅读
JDK 版本
JDK11
Sync
内部类 Sync 继承 AQS,实现相关方法
Sync(int permits) {
setState(permits);
}
final int getPermits() {
return getState();
}
构造时可以初始化 信号量 的个数,即最多同时被多少线程 共享
nonfairTryAcquireShared
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
/**
* 没有剩余资源则直接返回 负数
* 剩余资源 >= 0,则 CAS 修改 信号量 个数,修改成功则返回
*/
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
信号量 获取,如果剩余 信号量 不满足需求,则直接返回一个负数
如果 信号量 足够,则尝试 CAS 修改 state
,修改成功的线程即成功获取到 信号量,返回剩余 信号量 个数,其余线程继续 自旋 尝试
此处 自旋 即不停地 for循环
tryReleaseShared
protected final boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
int next = current + releases;
// 溢出
if (next < current)
throw new Error("Maximum permit count exceeded");
// CAS 修改 信号量 个数
if (compareAndSetState(current, next))
return true;
}
}
释放 信号量,在校验没有溢出后,CAS 修改 信号量 个数
其他方法
// 减少 信号量
final void reducePermits(int reductions) {
for (;;) {
int current = getState();
int next = current - reductions;
if (next > current) // underflow
throw new Error("Permit count underflow");
if (compareAndSetState(current, next))
return;
}
}
// 清空 信号量
final int drainPermits() {
for (;;) {
int current = getState();
if (current == 0 || compareAndSetState(current, 0))
return current;
}
}
同样的,Semaphore 也有 公平锁 和 非公平锁 版本
NonfairSync
static final class NonfairSync extends Sync {
private static final long serialVersionUID = -2694183684443567898L;
NonfairSync(int permits) {
super(permits);
}
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
}
非公平 实现,tryAcquireShared
委托给 nonfairTryAcquireShared
方法
FairSync
static final class FairSync extends Sync {
private static final long serialVersionUID = 2014338818796000944L;
FairSync(int permits) {
super(permits);
}
protected int tryAcquireShared(int acquires) {
for (;;) {
// 如果有节点排队,直接返回 -1
if (hasQueuedPredecessors())
return -1;
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
}
公平 实现,会在试图获取 信号量 之前,先判断是否有节点在排队,有则直接返回 -1
表示获取 信号量 失败
构造
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
默认构造 非公平 实现,且允许传入 permits
参数初始化 信号量 个数,fair
为 true,则构造 公平 实现
方法实现(委托)
// 获取 信号量(1个),响应中断
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
// 获取 信号量(1个),不响应中断
public void acquireUninterruptibly() {
sync.acquireShared(1);
}
// 获取信号量,是否成功
public boolean tryAcquire() {
return sync.nonfairTryAcquireShared(1) >= 0;
}
// 限时获取,超时则失败
public boolean tryAcquire(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
// 释放 信号量(1个)
public void release() {
sync.releaseShared(1);
}
// 获取 permits 个信号量,响应中断
public void acquire(int permits) throws InterruptedException {
if (permits < 0) throw new IllegalArgumentException();
sync.acquireSharedInterruptibly(permits);
}
// 获取 permits 个信号量,不响应中断
public void acquireUninterruptibly(int permits) {
if (permits < 0) throw new IllegalArgumentException();
sync.acquireShared(permits);
}
// 获取 permits 个信号量是否成功
public boolean tryAcquire(int permits) {
if (permits < 0) throw new IllegalArgumentException();
return sync.nonfairTryAcquireShared(permits) >= 0;
}
// 限时获取 permits 个信号量,超时则失败
public boolean tryAcquire(int permits, long timeout, TimeUnit unit)
throws InterruptedException {
if (permits < 0) throw new IllegalArgumentException();
return sync.tryAcquireSharedNanos(permits, unit.toNanos(timeout));
}
// 释放 permits 个信号量
public void release(int permits) {
if (permits < 0) throw new IllegalArgumentException();
sync.releaseShared(permits);
}
// 剩余 信号量 个数
public int availablePermits() {
return sync.getPermits();
}
// 略
可以发现,不像 ReentrantLock 提供的 lock
和 unlock
方法,Semaphore 的 信号量 获取释放则直接调用 acquire
和 release
方法实现
demo
public class SemaphoreTest {
static Semaphore semaphore = new Semaphore(2, true);
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
new Thread(() -> {
try {
test();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "t" + i).start();
}
}
static void test() throws InterruptedException {
semaphore.acquire();
System.out.println(Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(3);
semaphore.release();
}
}
结果:
t0
t1
------- 停顿 3 秒 --------
t2
只初始化了 2 个 信号量,因此 t0 t1
线程可以获取到,待处理完逻辑 release
信号量后, t2
线程才能获取到 信号量
总结
共享锁 的经典实现,好用且有效