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
责编
| 小耶哥
本期作者
| 小耶哥
平台建设及技术支持 | 小耶哥