Skip to content

Commit adea895

Browse files
committed
fix(aqs.md): 修正Semaphore原理描述,增加tryAcquireShared 和 tryReleaseShared 源码
1 parent e18163e commit adea895

File tree

1 file changed

+43
-3
lines changed

1 file changed

+43
-3
lines changed

docs/java/concurrent/aqs.md

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,9 @@ public Semaphore(int permits, boolean fair) {
154154

155155
`Semaphore` 是共享锁的一种实现,它默认构造 AQS 的 `state` 值为 `permits`,你可以将 `permits` 的值理解为许可证的数量,只有拿到许可证的线程才能执行。
156156

157-
调用`semaphore.acquire()` ,线程尝试获取许可证,如果 `state >= 0` 的话,则表示可以获取成功。如果获取成功的话,使用 CAS 操作去修改 `state` 的值 `state=state-1`。如果 `state<0` 的话,则表示许可证数量不足。此时会创建一个 Node 节点加入阻塞队列,挂起当前线程。
157+
调用`semaphore.acquire()` ,线程尝试获取许可证,如果 `state > 0` 的话,则表示可以获取成功,如果 `state <= 0` 的话,则表示许可证数量不足,获取失败。
158+
159+
如果可以获取成功的话(`state > 0` ),会尝试使用 CAS 操作去修改 `state` 的值 `state=state-1`。如果获取失败则会创建一个 Node 节点加入阻塞队列,挂起当前线程。
158160

159161
```java
160162
/**
@@ -170,13 +172,40 @@ public final void acquireSharedInterruptibly(int arg)
170172
throws InterruptedException {
171173
if (Thread.interrupted())
172174
throw new InterruptedException();
173-
// 尝试获取许可证,arg为获取许可证个数,当可用许可证数减当前获取的许可证数结果小于0,则创建一个节点加入阻塞队列,挂起当前线程。
175+
// 尝试获取许可证,arg为获取许可证个数,当获取失败时,则创建一个节点加入阻塞队列,挂起当前线程。
174176
if (tryAcquireShared(arg) < 0)
175177
doAcquireSharedInterruptibly(arg);
176178
}
179+
/**
180+
* 共享模式下尝试获取资源(在Semaphore中的资源即许可证):
181+
* 1、获取失败,返回负值
182+
* 2、共享模式下获取成功,但后续的共享模式获取会失败,返回0
183+
* 3、共享模式获取成功,随后的共享模式也可能获取成功,返回正值
184+
*/
185+
protected int tryAcquireShared(int acquires) {
186+
return nonfairTryAcquireShared(acquires);
187+
}
188+
/**
189+
* 非公平的共享模式获取许可证,acquires为许可证数量,根据代码上下文可知该值总是为1
190+
* 注:公平模式的实现会先判断队列中是否有节点在排队,有则直接返回-1,表示获取失败,没有则执行下面的操作
191+
*/
192+
final int nonfairTryAcquireShared(int acquires) {
193+
for (;;) {
194+
// 当前可用许可证数量
195+
int available = getState();
196+
/*
197+
* 尝试获取许可证,当前可用许可证数量小于等于0时,返回负值,表示获取失败,
198+
* 当前可用许可证大于0时才可能获取成功,CAS失败了会循环重新获取最新的值尝试获取
199+
*/
200+
int remaining = available - acquires;
201+
if (remaining < 0 ||
202+
compareAndSetState(available, remaining))
203+
return remaining;
204+
}
205+
}
177206
```
178207

179-
调用`semaphore.release();` ,线程尝试释放许可证,并使用 CAS 操作去修改 `state` 的值 `state=state+1`。释放许可证成功之后,同时会唤醒同步队列中的一个线程。被唤醒的线程会重新尝试去修改 `state` 的值 `state=state-1` ,如果 `state>=0` 则获取令牌成功,否则重新进入阻塞队列,挂起线程。
208+
调用`semaphore.release();` ,线程尝试释放许可证,并使用 CAS 操作去修改 `state` 的值 `state=state+1`。释放许可证成功之后,同时会唤醒同步队列中的一个线程。被唤醒的线程会重新尝试去修改 `state` 的值 `state=state-1` ,如果 `state > 0` 则获取令牌成功,否则重新进入阻塞队列,挂起线程。
180209

181210
```java
182211
// 释放一个许可证
@@ -194,6 +223,17 @@ public final boolean releaseShared(int arg) {
194223
}
195224
return false;
196225
}
226+
// 尝试释放资源
227+
protected final boolean tryReleaseShared(int releases) {
228+
for (;;) {
229+
int current = getState();
230+
int next = current + releases; // 可用许可证+1
231+
if (next < current) // overflow
232+
throw new Error("Maximum permit count exceeded");
233+
if (compareAndSetState(current, next)) // 通过CAS修改
234+
return true;
235+
}
236+
}
197237
```
198238

199239
#### 实战

0 commit comments

Comments
 (0)