AQS的许可证颁发、利用AQS实现独占锁
11-6 AQS的许可证颁发
AQS在Semaphore的应用
在
Semaphore中,state表示
许可证
的
剩余数量
11-6-1
Semaphore
源码
主要方法
l
Acquire
: 获取许可证
l
Release
: 释放许可证
acquire
public void acquire(int permits) throws InterruptedException {
if (permits < 0) throw new IllegalArgumentException();
sync.acquireSharedInterruptibly(permits);
}
acquireSharedInterruptibly
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
tryAcquireShared
有两种实现
, 公平和不公平
NonfairSync
extends
Sync
非公平情况
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 || compareAndSetState(available, remaining))
return remaining;
}
}
主要作用
:
获取许可证, 其返回值
正数
: 代表获取成功,
负数
: 获取失败
首先获取当前可用许可证的数量
int
available = getState();
其次计算当前可用许可证数量是否满足本次要求
,
acquires
想要获取许可证的数量
只要当前可用许可证数量
-
想要获得的许可证数量
<
0, 就会返回负数;
上一个调用该方法的方法就会将当前线程放入等待队列中等待
11-6-2
AQS在Semaphore的应用
l
在
Semaphore中,
state表示许可证的剩余数量
,
l
看
tryAcquire
()
方法,判断
nonfairTryAcquireShared大于等于0的话,代表成功
l
这里会先检查剩余许可证数量够不够这次需要的,用减法来计算,如果直接不够,那就返回负数,表示失败,如果够了,就用自旋
+
compareAndSetState
来改变
state状态,直到改变成功就返回正数;
或者是期间如果被其他人修改了
,
导致剩余数量不够了,那也返回负数代表获取失败
11-7 利用AQS实现独占锁
AQS在ReentrantLock的应用
分析释放锁的方法
tryRelease
11-7-1
ReentrantLock
|
unlock
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
第一步就调用了
tryRelease
方法
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
判断当前线程是不是持有锁的线程
Thread.currentThread() != getExclusiveOwnerThread()
只有持有锁的线程才能解锁
, 否则抛出异常
当前
state数量
-
需要释放的数量
=
0
的情况下才会去释放锁
int c = getState() - releases;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
tryRelease
方法返回
true以后, 调用该方法者就会执行如下代码
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
唤醒后续节点
unparkSuccessor
11-7-2
ReentrantLock
|
lock
public void lock() {
sync.lock();
}
abstract void lock();
此处是一个抽象方法
, 没有被实现
因为根据公平
/非公平有两种锁的策略

以非公平为例
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
首先进行
CAS操作
if (compareAndSetState(0, 1))
期望值是
0, 意味着当前没有任何人持有这把锁的时候, state为0的时候才能加锁成功
并且将当前线程设置为持有锁的线程
setExclusiveOwnerThread(Thread.currentThread())
另一个分支是
acquire
acquire
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
首先调用
tryAcquire
tryAcquire
实现在
ReentrantLock类中
非公平为例
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
判断该锁是否被持有
int c = getState()
c == 0
, 说明无人持有该锁, 通过CAS将该锁持有, 设置当前线程持有该锁
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
c != 0
并且当前线程是该锁的持有者
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
则说明本次获取锁
, 对于当前线程来说是一个重入操作, 所以stat =
c + acquires
然后设置
stat值
setState(nextc);
c != 0 并且当前线程
不是
该锁的持有者
则代表该锁已经被其他线程持有了
, 则获取失败
11-7-3 AQS在ReentrantLock的应用
l
分析
释放锁
的方法
tryRelease
()
l
由于是可重入的,所以
state代表重入的次数
,每次释放锁,先判断是不是当前持有锁的线程释放的,如果不是就抛异常,如果是的话,重入次数就减
1
,如果减到了
0,就说明完全释放了,于是free就是true,并且把state设置为0。
l
加锁的方法
近期热文
JUC1 线程池【治理线程的最大法宝】线程池简介+增加线程池
JUC2 线程池【治理线程的最大法宝】keepAliveTime+内存溢出+newSingleThreadExecutor
JUC3 线程池【治理线程的最大法宝】对比线程池 收服线程池
JUC 4 线程池【治理线程的最大法宝】钩子方法 Executor相关类 线程池状态
JUC5 ThreadLocal一网打尽(1)
JUC6 ThreadLocal一网打尽 (2)
JUC7 AQS, Semaphore和AQS
JUC8 AQS三要素及简要分析
参考资料
1 | JUC
责编
| 小耶哥
本期作者
| 小耶哥
平台建设及技术支持
| 小耶哥
