AQS分析
lock.lock()过程
1.锁的基本要素:一个共享的的数据来记录锁的状态,AQS采用State作为锁标记,0为无锁,大于等于1是有锁状态
2.NonfairSync类代码片段
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
(1)获得锁成功:
compareAndSetState是CAS->乐观锁,原子性
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
即将expect和stateOffset相比如果相等则更新为update否则不更新,整个过程是原子的
compareAndSetState(0, 1)为true则获得锁成功执行,即将当前线程获得独占锁:
setExclusiveOwnerThread(Thread.currentThread());
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
(2)获得锁失败:
如果获得锁失败则走else,acquire(1);
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
①首先分析tryAcquire(arg):
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;
}
首先获取一次锁的状态,如果为无锁状态则采用CAS将当前线程设置为有锁状态,返回true
这样好处在于,线程执行效率如果快的话,再比较一次可能更加的有效率,每一个线程都可以在这再尝试一次是否可获得锁,这也是一种插队机制。
若不为无锁状态,则进行判断是否为重入锁,如果是则走else if一下逻辑,此段主要是对重入锁进行重入次数的计数并设置State的值
若以上两段都不满足则返回false,并且由于!tryAcquire(arg)所以走到了这一段代码acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
②分析acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
首先分析addWaiter(Node.EXCLUSIVE) 参数说明:Node.EXCLUSIVEnull arg1
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
//尝试快速询问,故障时备份到完整的询问
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
先来分析enq(node)
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
For(;;)代表死循环,以下图为例说明:
在第一次循环时,tail==null,所以走第一个if,然后紧接着CAS比较Head是否和null相等,如果相等则将Head设置为一个空的Node即Node(){} ;所以如上图step2所示,head指向了一个空节点Node,最后将tail = head。
在第二次循环时,tail已不为空(指向空节点),走else语句:代码如下所示
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
node为形参传入的node,它包装了那个线程,例如ThreadB,以ThreadB为例演示以下过程,首先node.prev指向t; CAS比较t跟内存中的tail是否相等,若相等则将tail更新为node即指向node,t.next=node,返回t;请对照上图step3看
若ThreadC进来,则不会进入enq(node)方法了,直接在以下代码处返回
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail; //此处为ThreadC进来
if (pred != null) { //此处不为空
node.prev = pred; //将node的prev指向上一个尾节点
if(compareAndSetTail(pred, node)){ //将尾节点换成当前节点Node(ThreadC)
pred.next = node; //pred的next指向Node(ThreadC)
return node; //返回Node(ThreadC)
}
}
enq(node);
return node;
}
此时会返回到上面的②acquireQueued(addWaiter(Node.EXCLUSIVE), arg)),则第一个Thread传递的为acquireQueued(Node(ThreadB),1);现在开始分析acquireQueued:
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) { //首先是死循环
final Node p = node.predecessor(); //在下面进行分析1),返回prev节点
if (p == head && tryAcquire(arg)) { //若第一次p==head为true 2)
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) && //下面分析这个if 3)
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
1)node.predecessor()代码:
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
判断prev是否为null,若是报错,否则返回p
2)tryAcquire代码:再次去尝试获取锁的那段代码
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;
}
3)shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()
1.shouldParkAfterFailedAcquire(p, node)
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus; //ws默认为0
if (ws == Node.SIGNAL) // SIGNAL为-1
return true;
if (ws > 0) { // 大于0的只有CANCELLED==1
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL); //将状态设置为SIGNAL
}
return false;
}
2.parkAndCheckInterrupt()若想进入此方法,一定是第二个线程过来时
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this); //将当前线程挂起
return Thread.interrupted(); //返回是否被interrupt过,若是则返回true,并复位,默认为false
}
此时线程被挂起,如下图step4所示
由下面的lock.unlock()中的unpark()时
回到这段代码
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) { //此时满足,因为在lock.unlock()执行unpark之后
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
tryAcquire(arg) ThreadB抢占到锁,并改变state和exclusiveOwnerThread为自己,如下图代码所示:
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
分析这一段代码
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
第一步:SetHead做的事情将Head指向ThreadB,将ThreadB中的thread属性设置为null,将指向之前的head节点设置为指向null
private void setHead(Node node) {
head = node;
node.thread = null;
node.prev = null;
}
第二步:将p的next属性设置为null,最终如下图所示:
lock.unlock()过程
猜测一下,首先释放锁肯定会将锁状态设置为0,以及线程设置为空,其次肯定会唤醒下一个等待的节点,使得去抢占锁
1 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;
}
1.1 tryRelease(arg)
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;
}
将exclusiveOwnerThread设置为null,将state设置为0,即猜测第一步
1.2 继续往下走
Node h = head;
if (h != null && h.waitStatus != 0) //满足条件
unparkSuccessor(h);
return true;
1.3 unparkSuccessor(h)
private void unparkSuccessor(Node node) {
int ws = node.waitStatus; // ws==-1
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0); //将head节点的waitstatus设置为0
Node s = node.next; //s即为ThreadB
if (s == null || s.waitStatus > 0) { //s不为null
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread); //走到这,将ThreadB唤醒
}
前面代码遗留两个问题
1
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
为什么要在尾部向后遍历找到实际的,因为可能会出现下图情况
若从前往后找有可能会出现next没引用上的问题
2
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
在此处被interrupt中断后,在这里相应:
static void selfInterrupt() {
Thread.currentThread().interrupt();
}