JUC源码分析-AQS

AbstractQueuedSynchronizer分析

AQS独占锁方法分析

互斥模式也可以称为独占模式,独占锁是互斥模式的实现(互斥模式的代码 在重入锁-Reetrantlock有讲解,这里不再详述)

//互斥模式获取锁的模板方法, 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共享锁方法分析

共享锁是共享模式的实现。

关键逻辑:唤醒一个节点后获取锁后,要唤醒下个节点来检查是否可以获得锁,如果可以不能获得锁,进入等待,如果能获取锁,则再唤醒下个节点检查

//doAcquireShared方法,阻塞和非阻塞过程中被打断,不抛出打断异常,只是记录打断状态(通过selfInterrupt())获取锁的流程与acquireQueued相似

 

  1. 在等待队列中加入代表当前等待节点的node
  2. 如果当前节点是在队列头部,则尝试获取锁
  3. 设置当前节点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  比较复杂后面重点分析

//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();

}

 

}

释放共享锁方法

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;

}

}

传递策略分析

只有 ws==0的情况下,waitStatus会从0改成PROPAGATE,有以下2种情况

新加入的节点waitStatus被doReleaseShared改为PROPAGATE

释放后的的节点doReleaseShared并发改成PROPAGATE

这2种情况有共同的特点,就是没有执行解除阻塞就获得了锁,推断出只有这种情况才需要传递。

setHeadAndPropagate中执行传递策略的条件是 h.waitStatus < 0,那么SIGNAL也符合,这正是让人迷糊的地方。假设h.waitStatus ==SIGNAL 并且h是head节点,说明这个节点刚入队后 doAcquireShared 自旋转的第一轮没有获得锁,随后waitStatus ==SIGNAL后第二轮自旋转获得了锁,说明 在将waitStatus 从0改为SIGNAL的期间或后面执行了doReleaseShared,其实也符合没有执行解除阻塞就获得了锁。

传递策略总结:执行了释放共享锁,然后没有执行解除阻塞就获得了锁才需要传递,PROPAGATE ,SIGNAL这2个状态值都符合这个要求。

 

获取共享锁打断方法,如果被唤醒后被打断抛出打断异常

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)

总结:AQS定义了互斥模式和共享模式获取和释放锁的模板方法, 通过CAS获取和释放锁的方法由子类实现。

 

 

你可能感兴趣的:(JUC源码分析)