AQS简介
首先我们认识下AQS(AbstractQueuedSynchronizer),是Java并发的一个框架,比如说一些并发类: 独占锁 : ReentrantLock锁(独占) 共享锁 Semaphore信号量、CoutDownLatch计数器、CyclicBarrier(循环栅栏)等... AQS核心是通过FIFO等待的队列来实现线程的阻塞和唤醒的,分别有两种队列Condition和CLH等待队列;
AQS三个关键定义
- state:标记共享变量,volatile修饰,可重入;
arduino
//state =0 表示当前锁没有被占用,state>0表示被占用
private volatile int state;
- queue:线程获取锁失败后,进入queue中排队等待唤醒
- cas:比较然后交换,修改state的值; 这里存在三个参数分别是:内存值、预期值以及新值 执行逻辑:比较内存值与预期值,如果相等则去更新新值,如果不等则返回失败;
关于AQS的队列结构,之前写过AQS为什么使用双向FIFO队列 - 掘金,这里就不做详细介绍了;
AQS加锁过程
关于加锁,主要是依靠ReentrantLock类,通过它的tryAcquire方法尝试获取锁
java
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
//尝试加锁
protected final boolean tryAcquire(int acquires) {
//获取当前线程对象
final Thread current = Thread.currentThread();
//获取当前state值
int c = getState();
//判断是否被占用
if (c == 0) {
//多个线程请求锁,利用cas来更新
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
//更新成功说明就获取到了锁对象,设置当前线程持有锁对象
setExclusiveOwnerThread(current);
//获取锁成功
return true;
}
}
//如果当前线程已经持有锁,因为可重入,可以继续state ++
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
//可重入锁,获取成功
return true;
}
//没有获取锁成功,返回false
return false;
}
}
addWaiter
scss
public final void acquire(int arg) {
//获取锁失败,调用addWaiter进入等待队列
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
如果返回false加锁失败,直接就丢弃么,不是的,需要加入到queue中等待队列进行等待,静态内部类Node的addWaiter方法;
ini
private Node addWaiter(Node mode) {
//将当前线程封装成Node
Node node = new Node(Thread.currentThread(), mode);
//获取尾节点
Node pred = tail;
//不为空
if (pred != null) {
//将prev节点设置为tail尾节点
node.prev = pred;
//进行cas,尾节点和tail进行交换
if (compareAndSetTail(pred, node)) {
//tail的next节点变成node
pred.next = node;
return node;
}
}
//入队不成功 执行enq方法
enq(node);
return node;
}
enq方法
ini
private Node enq(final Node node) {
//死循环
for (;;) {
//将node设置为尾节点
Node t = tail;
//如果为空
if (t == null) {
//创建新的Node新节点,设置为head
if (compareAndSetHead(new Node()))
//经过循环node将到队列尾部
tail = head;
} else {
node.prev = t;
//cas将node设置为tail
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
LockSupport
park阻塞
上面说了加锁失败的线程都将进入队列等待,这时候就出现了一个park的概念,LockSupport类的park方法,对AQS中的node线程进行阻塞,阻塞的主要目的是:防止自旋一直获取锁消耗CPU的资源,需要的时候可以调用unpark方法唤醒线程;
scss
public static void park(Object blocker) {
//获取当前线程
Thread t = Thread.currentThread();
setBlocker(t, blocker);
//调用UNSAFE的park方法,将线程进入休眠等待状态
UNSAFE.park(false, 0L);
setBlocker(t, null);
}
unpark唤醒
唤醒需要被唤醒的线程
arduino
public static void unpark(Thread thread) {
//传入需要被唤醒的线程
if (thread != null)
UNSAFE.unpark(thread);
}
Condition队列
上面说了线程的阻塞和唤醒,那么需要被阻塞和唤醒的线程要是需要搭载在队列上面的,所以AQS就提供了Condition队列;
Wait
重要的两个节点condition的头节点和尾节点
java
//condition对应的队列的头节点
private transient Node firstWaiter;
//condition对应的队列的尾节点
private transient Node lastWaiter;
await主要是是将node阻塞线程进行入列操作
scss
public final void await() throws InterruptedException {
//线程中断抛出异常
if (Thread.interrupted())
throw new InterruptedException();
//加入等待队列
Node node = addConditionWaiter();
//将锁释放 如果释放失败就将waitstatus状态改完CANCELLED
long savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
//进行阻塞
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
//已经唤醒的线程,需要被提出队列
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
unlinkCancelledWaiters剔除不为conditon节点的node
ini
private void unlinkCancelledWaiters() {
//获取等待队列
Node t = firstWaiter;
Node trail = null;
//不为空就一直循环
while (t != null) {
Node next = t.nextWaiter;
//waitStatus不为CONDITION队列
if (t.waitStatus != Node.CONDITION) {
//切断不为condition节点的下一个节点,切断它们之间的关系
t.nextWaiter = null;
//如果trail 为空,firstWaiter也不是CONDITION节点,需要被剔除
if (trail == null)
firstWaiter = next;
else
trail.nextWaiter = next;
if (next == null)
lastWaiter = trail;
}
else
trail = t;
t = next;
}
}
Signal
前面说线程加入等待队列,下面来看线程是如何被唤醒的
java
public final void signal() {
//判断当前线程是否持有锁
if (!isHeldExclusively())
// 没有持有就抛出异常
throw new IllegalMonitorStateException();
//获取condition里面第一个节点
Node first = firstWaiter;
//如果不为空 调用doSignal
if (first != null)
doSignal(first);
}
transferForSignal方法
typescript
private void doSignal(Node first) {
do {
//循环,如果队列的第一个和下一个为空,则将后续节点都剔除
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
//不为空调用transferForSignal方法
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
transferForSignal节点唤醒
arduino
final boolean transferForSignal(Node node) {
//将nodewaitStatus从CONDITION改为0
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
//返回firstWaiter前节点
Node p = enq(node);
int ws = p.waitStatus;
// 如果前节点的waitStatus > 0
//或者将前节点waitStatus改成 Node.SIGNA 失败了,也会触发节点唤醒
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
SignalAll(唤醒condition全节点)
方法大致差不多,就是调用了signalAll方法
java
public final void signalAll() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignalAll(first);
}
doSignalAll
ini
private void doSignalAll(Node first) {
lastWaiter = firstWaiter = null;
do {
Node next = first.nextWaiter;
first.nextWaiter = null;
transferForSignal(first);
first = next;
} while (first != null);
}
checkInterruptWhileWaiting(唤醒后的操作)
唤醒后干嘛呢,要看这里了,checkInterruptWhileWaiting方法
checkInterruptWhileWaiting这一步是检查唤醒线程的状态,根据不同的状态进行设置异常或者中断
arduino
private int checkInterruptWhileWaiting(Node node) {
return Thread.interrupted() ?
(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
0;
}
- 如果线程中断,设置为/THROW_IE/ ,抛出异常
- 如果signal没有发生异常,则设置为/REINTERRUPT/
- 如果线程没有中断就返回0
transferAfterCancelledWait
java
final boolean transferAfterCancelledWait(Node node) {
//将node的WaitStatus从Node.CONDITION改为0
if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
enq(node);
return true;
}
while (!isOnSyncQueue(node))
Thread.yield();
return false;
}
acquireQueued抢夺锁
ini
final boolean acquireQueued(final Node node, long arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
//如果Node是头节点,就去尝试获取锁
if (p == head && tryAcquire(arg)) {
setHead(node);
//帮助gc
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
// 如果抢占锁失败,就将线程从队列清除
if (failed)
cancelAcquire(node);
}
}
- acquireQueued不出现异常的话,就将interruptMode设置为/REINTERRUPT/
- node.nextWaiter != null ,就是没有经过唤醒,就去清除它
- interruptMode !=0 调用reportInterruptAfterWait方法
interruptMode为异常状态就抛出异常,状态为REINTERRUPT,调用中断方法
java
private void reportInterruptAfterWait(int interruptMode)
throws InterruptedException {
if (interruptMode == THROW_IE)
throw new InterruptedException();
else if (interruptMode == REINTERRUPT)
selfInterrupt();
}
总结
基于可重入锁ReentrantLock加锁过程,出现的condition线程等待队列,在队列中根据node节点状态,来决定阻塞还是被唤醒,释放锁唤醒queue上的其他节点来争夺锁,线程唤醒后会在condition队列中进行剔除,将waitStatus状态改为0,加锁锁的queue;