AbstractQueuedSynchronizer分析
AQS独占锁方法分析
互斥模式也可以称为独占模式,独占锁是互斥模式的实现(互斥模式的代码 在公平锁和非公平锁有讲解,这里不再详述)
//互斥模式获取锁的模板方法, tryAcquire 尝试通过CAS方式获取锁,由子类实现。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
//互斥模式获取锁的模板方法,如果当前线程被打断,抛出打断异常。
public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
//互斥模式获取锁的模板方法,如果指定纳秒内没有获得锁则中断获取,返回false。
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquire(arg) ||
doAcquireNanos(arg, nanosTimeout);
//互斥模式释放锁的模板方法,tryRelease(arg) 通过CAS释放锁,由子类实现
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
//共享模式获取锁的模板方法,tryAcquireShared 通过cas方式获取共享锁,由子类实现
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
与互斥模式相同 还有 acquireSharedInterruptibly,tryAcquireSharedNanos,releaseShared这几个模板,下面重点分析。
AQS共享锁方法分析
共享锁是共享模式的实现。
关键逻辑:唤醒一个节点后获取锁后,要唤醒下个节点来检查是否可以获得锁,如果可以不能获得锁,进入等待,如果能获取锁,则再唤醒下个节点检查
//获取共享锁不打断方法,阻塞过程中被打断,抛出打断异常,如果被唤醒后被打断不抛出打断异常,只是记录打断状态(通过selfInterrupt() )获取锁的流程与acquireQueued相似
- 在等待队列中加入代表当前等待节点的node
- 如果当前节点是在队列头部,则尝试获取锁
- 设置当前节点prev节点waitStatus状态为SIGNAL,重试执行步骤2,获取不到锁进入阻塞
private void doAcquireShared(int arg) {
//addWaiter 中 new Node(Thread.currentThread(), mode); 传入nextWaiter 属性 有什么用?
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head) {
//通过cas获取锁
int r = tryAcquireShared(arg);
if (r >= 0) {
//设置node为head,如果有必要则唤醒后续节点。
//与互斥模式不同,共享模式下可以有多个线程持有锁,如果一个线程获得锁后,排队的线程获得锁的可能性是很大的,需要另外的解锁线程去尝试获取锁,以便减少锁等待的时间,增加程序执行效率,可以理解为传递策略的实现。
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
setHead(node);
// propagate > 0 有空位置
//h.waitStatus < 0 释放锁方法doReleaseShared
中把0修改成PROPAGATE。根据 doAcquireShared的逻辑 waitStatus 的值可能为0, SIGNAL ,so doReleaseShared == 0 or SIGNAL or PROPAGATE 。当 h.waitStatus的值等于负数时,说明node是新加入的节点经过一轮自旋waitStatus改为SIGNAL且 doReleaseShared正在执行还没修改waitStatus, 或新加入的节点waitStatus被doReleaseShared改为PROPAGATE ,这2种情况都是还没进入阻塞就获得了锁。h.waitStatus==0 说明是新加入的节点 且未执行doReleaseShared修改 h.waitStatus,也可能是被唤醒后被改为0(情况A)。
//h==null的情况 目前来看不可能
if (propagate > 0 || h == null || h.waitStatus < 0) {
Node s = node.next;
//s == null 证明当前节点是尾部节点,但是 有可能会立即加入新节点,s.isShared()指明是共享模式。这两种情况都需要释放后续节点(按照doAcquireShared中重点部分讲解到的 传递策略)。
if (s == null || s.isShared())
doReleaseShared();
}
//只有情况A和 独占模式不需要传递
//猜想如果出现了情况A 虽然违背了设计初衷,但并不是bug,后续的释放 还会后续的节点并执行传递。
}
总结上面的判断 大概的意思是只要释放了锁就要执行传递策略
获取共享锁打断方法,如果被唤醒后被打断抛出打断异常
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
//addWaiter 中 new Node(Thread.currentThread(), mode); 传入nextWaiter 属性 有什么用?
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
获取共享锁timeout(单位纳秒)打断方法,如果被唤醒后被打断抛出打断异常
private boolean doAcquireSharedNanos(int arg, long nanosTimeout)
释放共享锁方法
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
//传递策略的原因 有可能出现并发,利用CAS解决。
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
//PROPAGATE的用途?? 传递状态标识用于执行传递策略。
//如果不使用PROPAGATE可以么??,貌似没有办法 因为unparkSuccessor方法是通用的,解锁后ws=0,这点没办法改变,要标识需要执行传递策略必须定义一个标识值。
// 当第一个节点刚刚加入队列时,ws==0 这时如果没有获取锁 ws值 0->PROPAGATE>SIGNAL。其中PROPAGATE>SIGNAL在shouldParkAfterFailedAcquire执行。获得到了锁 唤醒下一个头节点。
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
总结:AQS从代码实现上看定义了互斥模式和共享模式获取和释放锁的模板方法,CAS的部分,即 通过CAS获取和释放锁的方法由子类实现。
来源:CSDN
作者:chenchangqun11
链接:https://blog.csdn.net/chenchangqun11/article/details/103913495