1
0
mirror of https://github.com/Snailclimb/JavaGuide synced 2025-06-16 18:10:13 +08:00

fix(aqs.md): 修正Semaphore原理描述,增加tryAcquireShared 和 tryReleaseShared 源码

This commit is contained in:
godelgnis 2023-05-21 00:01:45 +08:00
parent e18163ec9c
commit adea89547a

View File

@ -154,7 +154,9 @@ public Semaphore(int permits, boolean fair) {
`Semaphore` 是共享锁的一种实现,它默认构造 AQS 的 `state` 值为 `permits`,你可以将 `permits` 的值理解为许可证的数量,只有拿到许可证的线程才能执行。
调用`semaphore.acquire()` ,线程尝试获取许可证,如果 `state >= 0` 的话,则表示可以获取成功。如果获取成功的话,使用 CAS 操作去修改 `state` 的值 `state=state-1`。如果 `state<0` 的话,则表示许可证数量不足。此时会创建一个 Node 节点加入阻塞队列,挂起当前线程。
调用`semaphore.acquire()` ,线程尝试获取许可证,如果 `state > 0` 的话,则表示可以获取成功,如果 `state <= 0` 的话,则表示许可证数量不足,获取失败。
如果可以获取成功的话(`state > 0` ),会尝试使用 CAS 操作去修改 `state` 的值 `state=state-1`。如果获取失败则会创建一个 Node 节点加入阻塞队列,挂起当前线程。
```java
/**
@ -170,13 +172,40 @@ public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// 尝试获取许可证arg为获取许可证个数可用许可证数减当前获取的许可证数结果小于0,则创建一个节点加入阻塞队列,挂起当前线程。
// 尝试获取许可证arg为获取许可证个数获取失败时,则创建一个节点加入阻塞队列,挂起当前线程。
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
/**
* 共享模式下尝试获取资源(在Semaphore中的资源即许可证):
* 1、获取失败返回负值
* 2、共享模式下获取成功但后续的共享模式获取会失败返回0
* 3、共享模式获取成功随后的共享模式也可能获取成功返回正值
*/
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
/**
* 非公平的共享模式获取许可证acquires为许可证数量根据代码上下文可知该值总是为1
* 注:公平模式的实现会先判断队列中是否有节点在排队,有则直接返回-1表示获取失败没有则执行下面的操作
*/
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
// 当前可用许可证数量
int available = getState();
/*
* 尝试获取许可证当前可用许可证数量小于等于0时返回负值表示获取失败
* 当前可用许可证大于0时才可能获取成功CAS失败了会循环重新获取最新的值尝试获取
*/
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
```
调用`semaphore.release();` ,线程尝试释放许可证,并使用 CAS 操作去修改 `state` 的值 `state=state+1`。释放许可证成功之后,同时会唤醒同步队列中的一个线程。被唤醒的线程会重新尝试去修改 `state` 的值 `state=state-1` ,如果 `state>=0` 则获取令牌成功,否则重新进入阻塞队列,挂起线程。
调用`semaphore.release();` ,线程尝试释放许可证,并使用 CAS 操作去修改 `state` 的值 `state=state+1`。释放许可证成功之后,同时会唤醒同步队列中的一个线程。被唤醒的线程会重新尝试去修改 `state` 的值 `state=state-1` ,如果 `state > 0` 则获取令牌成功,否则重新进入阻塞队列,挂起线程。
```java
// 释放一个许可证
@ -194,6 +223,17 @@ public final boolean releaseShared(int arg) {
}
return false;
}
// 尝试释放资源
protected final boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
int next = current + releases; // 可用许可证+1
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
if (compareAndSetState(current, next)) // 通过CAS修改
return true;
}
}
```
#### 实战