AQS解析以ReentrantLock公平锁为例
AQS中进程节点的状态
线程的2种等待模式:
- SHARED:表示线程以共享的模式等待锁(如ReadLock)
- EXCLUSIVE:表示线程以互斥的模式等待锁(如ReentrantLock),互斥就是一把锁只能由一个线程持有,不能同时存在多个线程使用同一个锁
线程在队列中的状态枚举:
- CANCELLED:值为1,表示线程的获锁请求已经“取消”
- SIGNAL:值为-1,表示该线程一切都准备好了,就等待锁空闲出来给我(比如现在头节点的值为-1,那么说明头节点的下一个节点的线程正在临界区,当其退出临界区的时候会释放锁,此时这个线程成为头节点,并查看自己的节点值是否为-1,如果为-1,说明之后有线程等待(线程插入的时候修改前面一个节点的值为-1),那么唤醒这个线程)。
- CONDITION:值为-2,表示线程等待某一个条件(Condition)被满足
- PROPAGATE:值为-3,当线程处在“SHARED”模式时,该字段才会被使用上(在后续讲共享锁的时候再细聊)
初始化Node对象时,默认为0
成员变量:
- waitStatus:该int变量表示线程在队列中的状态,其值就是上述提到的CANCELLED、SIGNAL、CONDITION、PROPAGATE
- prev:该变量类型为Node对象,表示该节点的前一个Node节点(前驱)
- next:该变量类型为Node对象,表示该节点的后一个Node节点(后继)
- thread:该变量类型为Thread对象,表示该节点的代表的线程
- nextWaiter:该变量类型为Node对象,表示等待condition条件的Node节点(暂时不用管它,不影响我们理解主要知识点)
调用lock();
ReentrantLock lock = new ReentrantLock(true);
lock.lock();
会到
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
}
Sync类继承于AbstractQueuedSynchronizer
AQS中的acquire方法,其中arg传的参数是说申请的锁里面修改state的数量为1,对于排他锁来说,一次只能申请一个,而共享锁可以多次进入,所以state的数量可以大于1
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
会先执行tryAcquire方法来尝试是否可以获得锁
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
//获得当前锁的状态,如果是排他锁且没有线程拿到锁,那么为0,如果有线程申请上了锁,那么为大于1
int c = getState();
//锁空闲
if (c == 0) {
//hasQueuedPredecessors()方法非公平锁没有,是去判断是在此线程之前是否有其他线程在等待这个锁,如果有,那么线程会排队。如果没有这个方法的判断就是非公平锁,前一个线程释放锁之后通知后一个线程的时候,后一个线程还没有拿到锁,此时突然有另外一个B线程出来申请锁,那么可能锁会被这个B线程申请上。
//compareAndSetState(0, acquires)这个就是在申请锁,如果申请上了,那么会设置下一步setExclusiveOwnerThread(current),将当前拿到锁的线程保存一下。
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//如果是可重入锁的话,state是可以大于0的,所以判断一个是不是拿到锁的线程是自己
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
//直接修改state就可以了
setState(nextc);
return true;
}
//要不然的话申请失败
return false;
}
}
如果tryAcquire尝试没有获得到锁,会执行acquireQueued(addWaiter(Node.EXCLUSIVE), arg)),先看addWaiter(Node.EXCLUSIVE),其主要是生成本线程的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;
if (pred != null) {
//注意,先设置到末尾,这个对应于之后的unlock方法里的唤醒下一个排队线程时要从尾到头,因为下面的cas操作之后才会设置pred.next = node,不是一个原子的操作。
node.prev = pred;
//这里也是尝试设置一下,如果成功就非常好,直接设置就行
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//如果cas不成功,说明有其他线程的node也插入了,在enq中执行循环插入,直到成功为止。
//或者说, Node pred = tail;if (pred != null)这里,压根就没有链表,要新建立链表。
//注意这里的新建链表是新建一个空的头,如何把node指向后
enq(node);
return 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;
}
}
}
}
addWaiter执行完成后,会执行acquireQueued,其主要就是申请锁
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)) {
//如果拿到了锁,就把自己的节点当头
setHead(node);
p.next = null; // help GC
failed = false;
//成功拿到锁,返回interrupted,这之中如果设置了中断,之后也是要抛出的。
return interrupted;
}
//如果再次(第二次)尝试还是没有拿到锁或者根本前驱也没有拿到锁,那么会先进入这里shouldParkAfterFailedAcquire(p, node),如果返回true,执行parkAndCheckInterrupt()
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
shouldParkAfterFailedAcquire(p, node)中有
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
//拿到前驱的waitStatus,默认是0
int ws = pred.waitStatus;
//如果是第二次进入shouldParkAfterFailedAcquire的时候,说明还是没有拿到锁,因为已经将前驱节点的waitStatus设置成了Node.SIGNAL,那么会直接返回true,执行parkAndCheckInterrupt()
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
//第一次到这里的时候会给前驱设置Node.SIGNAL,也就是-1,
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
parkAndCheckInterrupt()中有:
private final boolean parkAndCheckInterrupt() {
// LockSupport.park(this);直接就是把这个线程阻塞了,如下
LockSupport.park(this);
// 之后如果唤醒了的话(node里存有thread,因为调用UNSAFE唤醒),会返回thread的interrupt这个属性,因为在阻塞的时候,有可能其他线程会把这个阻塞的线程的interrupt属性改变,那么就要之后抛出中断。
return Thread.interrupted();
}
//LockSupport
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, 0L);
setBlocker(t, null);
}
如果一直没有拿到,那么线程就阻塞了。
调用unlock()
当一个线程调用了unlock()之后,不仅要处理是否重入,还要唤醒之后链表中的线程。
public void unlock() {
sync.release(1);
}
又是到了AQS里
public final boolean release(int arg) {
if (tryRelease(arg)) {
//到这里,说明没有重入的锁了,现在开始通知之后链表的线程
Node h = head;
//如果之后链表有线程,且已经阻塞的话,会给其链表前一个线程的node的waitStatus设置成-1,此时才执行unparkSuccessor通知被阻塞的线程,不然的话链表后的线程就是活跃的,不需要唤醒。
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
先会执行tryRelease(arg)
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//如果为0的话,说明没有重入了,可以释放了
if (c == 0) {
free = true;
//将保存拿到锁的线程变量置空
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
执行unparkSuccessor(h)
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;
//这里要把头节点的waitStatus设置成0,为什么?
//因为唤醒之后,如果是非公平锁,如果有其他线程申请锁,那么也是可以拿到锁的,此时这一瞬间要这个唤醒的线程和那个其他线程进行竞争,如果竞争失败的话,那么这个唤醒的线程还需要再次执行之前的操作,所以他的前驱节点最好还是置成默认值0。
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
Node s = node.next;
//找后继节点,如果后继节点不存在(有可能是前面addWaiter设置后继节点的代码还没执行),或者waitStatus大于0(后继的线程获取lock取消,比如超时了),那么就需要再找后继的后继,直到找到第一个结束。
if (s == null || s.waitStatus > 0) {
s = null;
//从后往前找,因为前面addWaiter中是先设置后继指向前驱的,再cas,再前驱指向后继,所以cas成功设置的时候,一定有后继指向前驱设置好了。addWaiter先这里的节点连接操作并不是原子,也就是说在多线程并发的情况下,可能会出 现个别节点并没有设置 next 值,就失败了,所以从尾节点逆向遍历。
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
//找到后,唤醒。
LockSupport.unpark(s.thread);
}
Condition
当一个condition调用的时候,会把进程阻塞,还是叫挂起吧
ReentrantLock lock = new ReentrantLock(true);
Condition cond = lock.newCondition();
new Thread(new Runnable() {
@Override
public void run() {
lock.lock();
try {
cond.await();
System.out.println(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.unlock();
}
}).start();
await()方法
因为在调用condition的时候必拿到lock的锁,而调用了await()方法后线程会被挂起,执行不到lock.unlock(),那么需要在await()方法里把锁去除(state置0),并且通知后续的线程(把head node的next node unpark)
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// 当前线程加入Conditon等待队列
Node node = addConditionWaiter();
// 释放同步状态,即释放同步锁;唤醒当前线程的节点(头节点)的后继节点,因为调用condition.await()之后这个线程就会被阻塞,所以要通知后续阻塞的节点(是通知的锁那个链条上的节点)。
int savedState = fullyRelease(node);
int interruptMode = 0;
// 如果节点不在同步队列中,这个创建的node是放在condition当中的,不是放在lock的链表中的
// 之后唤醒的时候就在lock的链表中了,就可以退出了。
while (!isOnSyncQueue(node)) {
// 阻塞当前线程
LockSupport.park(this);
// 如果中断被唤醒,循环停止
// 判断此次线程的唤醒是否因为线程被中断,
// 若是被中断, 则会在checkInterruptWhileWaiting的transferAfterCancelledWait进行节点的转移;
// 返回值interruptMode != 0
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
// 调用 acquireQueued在Sync Queue里面进行独占锁的获取, 返回值表明在获取的过程中有没有被中断过
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
// 判断线程的唤醒是中断还是signal
// 如通过中断唤醒的话, 此刻代表线程的Node在Condition Queue与Sync Queue里面都会存在
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
// 通过中断的方式唤醒线程
if (interruptMode != 0)
// 根据 interruptMode 的类型决定是抛出异常, 还是自己中断一下
reportInterruptAfterWait(interruptMode);
}
这个LockSupport.park(this);中的this,指的是condtion这个实例,所以将这个线程和这个condtion绑定在了一起。
addConditionWaiter()方法比较简单,因为调用condtion的await()方法的时候已经拿到了锁,所以必须是一个一个的进的,所以不需要额外的cas
private Node addConditionWaiter() {
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
//注意,虽然生成的node和lock中的node一样,但是这个node是放在condition中的,也为了区分,设置waitstatues为Node.CONDITION,-2
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
fullyRelease方法也比较简单
final int fullyRelease(Node node) {
boolean failed = true;
try {
int savedState = getState();
if (release(savedState)) {
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
if (failed)
node.waitStatus = Node.CANCELLED;
}
}
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
再来看看condition.signalAll方法
使用:
new Thread(new Runnable() {
@Override
public void run() {
lock.lock();
cond.signalAll();
System.out.println(3);
lock.unlock();
}
}).start();
public final void signalAll() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignalAll(first);
}
isHeldExclusively是判断当前线程是否已经拿到了锁
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
//拿到锁之后,会将exclusiveOwnerThread属性设置为拿到锁的线程
protected final Thread getExclusiveOwnerThread() {
return exclusiveOwnerThread;
}
doSignalAll(first)方法
private void doSignalAll(Node first) {
lastWaiter = firstWaiter = null;
do {
Node next = first.nextWaiter;
first.nextWaiter = null;
transferForSignal(first);
first = next;
} while (first != null);
}
//对比一下signal方法,只是拿到第一个node,并确保加入了lock的链表中
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
transferForSignal方法
final boolean transferForSignal(Node node) {
/*
* If cannot change waitStatus, the node has been cancelled.
*/
//确保这个node一开始是 Node.CONDITION类型的,之后要加入lock中,所以设置为0
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
/*
* Splice onto queue and try to set waitStatus of predecessor to
* indicate that thread is (probably) waiting. If cancelled or
* attempt to set waitStatus fails, wake up to resync (in which
* case the waitStatus can be transiently and harmlessly wrong).
*/
//将node加入lock,返回的是加入之后node的前驱
Node p = enq(node);
int ws = p.waitStatus;
//修改前驱的waitStatus为Node.SIGNAL,如果设置不成功的话,直接唤醒node
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
doSignalAll将condition的node全部加入lock的链表中,调用完成后,会有lock.unlock(),之后就是走流程唤醒了。
而在调用唤醒后,线程会在await方法中,跳出while (!isOnSyncQueue(node))循环,执行接下来的任务:
但是首先会判断一下,是否是中断
// 如果中断被唤醒,循环停止
// 判断此次线程的唤醒是否因为线程被中断,
// 若是被中断, 则会在checkInterruptWhileWaiting的transferAfterCancelledWait进行节点的转移;
// 返回值interruptMode != 0
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
private int checkInterruptWhileWaiting(Node node) {
return Thread.interrupted() ?
(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
0;
}
//如果是中断唤醒,将node的放进lock的链表中
final boolean transferAfterCancelledWait(Node node) {
if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
enq(node);
return true;
}
/*
* If we lost out to a signal(), then we can't proceed
* until it finishes its enq(). Cancelling during an
* incomplete transfer is both rare and transient, so just
* spin.
*/
//如果上面的没有完成,说明有可能其他进程修改了node,那么调用Thread.yield()将线程变为就绪状态,直到其他线程把其放入lock的链表中
while (!isOnSyncQueue(node))
Thread.yield();
return false;
}
// 调用 acquireQueued在Sync Queue里面进行独占锁的获取, 返回值表明在获取的过程中有没有被中断过
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
// 判断线程的唤醒是中断还是signal
// 如通过中断唤醒的话, 此刻代表线程的Node在Condition Queue与Sync Queue里面都会存在,此时将Condition Queue的清理一下
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
// 通过中断的方式唤醒线程
if (interruptMode != 0)
// 根据 interruptMode 的类型决定是抛出异常, 还是自己中断一下
reportInterruptAfterWait(interruptMode);
acquireQueued
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)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
跟前面的acquireQueued是一个方法,拿到则ok,没拿到就挂起。
lockInterruptibly()和 interrupt()
ReentrantLock.lockInterruptibly允许在等待时(注意不是挂起)由其它线程调用等待线程的Thread.interrupt方法来中断等待线程的等待而直接返回,这时不用获取锁,而会抛出一个InterruptedException。 ReentrantLock.lock方法不允许Thread.interrupt中断,即使检测到Thread.isInterrupted,一样会继续尝试获取锁,失败则继续休眠。只是在最后获取锁成功后再把当前线程置为interrupted状态,然后再中断线程。