AQS
是什么?
- 一个抽象类 public abstract class AbstractQueuedSynchronizer
作用:
- 提供一个
框架
来实现依赖于先进先出(FIFO)等待队列
->CLH变种
的阻塞锁和相关的同步器(信号量、事件等)- Provides a framework for implementing blocking locks and related synchronizers (semaphores, events, etc) that rely on first-in-first-out (FIFO) wait queues.
FIFO双向队列
,AQS依赖它来完成同步状态的管理,当前线程如果获取同步状态失败时,AQS则会将当前线程已经等待状态等信息构造成一个节点(Node)并将其加入到CLH同步队列,同时会阻塞当前线程,当同步状态释放时,会把首节点唤醒(公平锁),使其再次尝试获取同步状态。
应用:
- 独占模式(排它)
ReentrantLock
、readWriteLock.writeLock()
- 共享模式
CountDownLatch
、Semaphore
、readWriteLock.readLock()
- 无论是独占锁还是共享锁,本质上都是对AQS内部的一个
变量state
的获取。state是一个原子的int变量,用来表示锁状态
、资源数
等
结构:
- 一个由
Node
组成的CLH
双向队列,以及一个内存可见int变量state
。
独占锁—实现大致流程:
tryAcquire
,尝试获取资源,获取后CAS更新state 0->1
,占有锁- 未占有锁线程则,进入
CLH队列
,加入排队- CAS设置当前节点
waitStatus
值0- > -1
从INITIAL -> SIGNAL
,表示当前线程阻塞
- 未释放锁 - ,线程再竞争
tail尾节点
不为null,CAS更新当前线程为尾节点,调整pre、next指向,CAS
compareAndSetWaitStatus(pred, ws, Node.SIGNAL)
设置前节点waitStatus
状态为SIGNAL
- 未释放锁 - , 判断当前节点,前节点
Node p = node.predecessor()
,如果为head节点,则尝试获取资源release
释放锁,getState() - releases=0,更新state
值为0unparkSuccessor(h)
唤醒下一个节点,如果node.next ==null || s.waitStatus > 0
,则从尾节点开始向前遍历, 直到找到距离head节点最近的waitStatus <=0
的节点,unpark()
,唤醒节点
抢锁:
- 尝试获取资源 -》
tryAcquire(arg)
,这定义的一个接口方法,等待被实现,结合state 变量的值实现
//独占式
//尝试获取资源 -》 tryAcquire(arg)
public final void acquire(int arg) {
//尝试获取资源tryAcquire(arg), 获取到则 !true ,不进入if
//如果state已经==1,有线程占用资源后,进入等候区 addWaiter(Node.EXCLUSIVE), arg)
if (!tryAcquire(arg) &&
//Node.EXCLUSIVE 标记独占式访问
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
tryAcquire(arg)
在ReentrantLock
中的实现
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
//获取state值
int c = getState();
if (c == 0) {
//为0则CAS更新值1,占有资源-获取锁
if (compareAndSetState(0, acquires)) {
//设置exclusiveOwnerThread 当前线程
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;
}
初始化队列-》进入等候区
private Node addWaiter(Node mode) {
//封装当前线程-》node
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
//首次节点为null ->enq(node)
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//初始化节点
enq(node);
return node;
}
/*
Inserts node into queue, initializing if necessary. See picture above.
Params:
node – the node to insert
Returns:
node's predecessor
*/
private Node enq(final Node node) {
// 如果CAS设置未成功则死循环
for (;;) {
Node t = tail;
// 如果尾节点为空,说明CLH队列为空,需要初始化
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
// 设置当前节点的前驱节点
node.prev = t;
// CAS设置当前节点为尾结点
if (compareAndSetTail(t, node)) {
// 设置后驱节点
t.next = node;
return t;
}
}
}
}
资源锁获取
- 首先获取当前节点的先驱节点,如果先驱节点是头结点的并且成功获得同步状态的时候(if (p == head && tryAcquire(arg))),当前节点所指向的线程能够获取锁
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);
// 取消前驱节点(以前的头部)的后节点,方便GC回收
p.next = null; // help GC
// 标识未失败
failed = false;
// 返回中断标志
return interrupted;
}
// 如果当前节点的前驱节点不是头结点或获取资源失败
// 需要用shouldParkAfterFailedAcquire函数判断是否需要阻塞该节点持有的线程
// 如果需要阻塞,则执行parkAndCheckInterrupt方法,并设置被中断
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
// 如果最终获取资源失败
if (failed)
// 当前节点取消获取资源
cancelAcquire(node);
}
}
//中断线程
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
当前线程是否阻塞判断 ,
pred.waitStatus
,通过前节点的waitStatus
,等待状态判断
- false -》不需要挂起
/** waitStatus value to indicate thread has cancelled */
//在同步队列中等待的线程等待超时或被中断,需要从同步队列中取消该Node的结点,
static final int CANCELLED = 1;
/** waitStatus value to indicate successor's thread needs unparking */
//只要前置节点释放锁,就会通知标识为SIGNAL状态的后续节点的线程
//每个等待的节点会将上一个节点的状态改为SIGNAL(-1),用来标志改节点后面的节点才可以被唤醒
static final int SIGNAL = -1;
/** waitStatus value to indicate thread is waiting on condition */
//此节点当前在条件队列中。标记为CONDITION的节点会被移动到一个特殊的条件等待队列(此时状态将设置为0),直到条件时才会被重新移动到同步等待队列
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
*/
//acquireShared 共享模式下,PROPAGATE状态的线程处于可运行状态
static final int PROPAGATE = -3;
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL) //如果前置节点为SIGNAL,意味着只需要等待其他前置节点的线程被释放
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
//true 挂起线程
return true;
if (ws > 0) {
// >0 prev节点取消了排队,直接移除这个节点
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
//采用循环,从双向列表中移除CANCELLED的节点
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.
*/
//使用CAS将节点从INITIAL -> SIGNAL,表示当前线程阻塞
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
锁释放:
//tryRelease(arg) 定义的一个抽象方法,由具体使用者实现,如 ReentrantLock
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
ReentrantLock 例子 -> tryRelease(arg)
- 上锁时,会CAS设置state为1 -》 getState() =1 , releases =1 ,c =0
- setExclusiveOwnerThread(null); 设置当前线程为null
- setState( c ); 设置state=0
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
//更新state值,当前线程置为null
if (tryRelease(arg)) {
Node h = head;
//waitStatus !=0 说明head节点后面没有在挂起等待中的后继节点了
if (h != null && h.waitStatus != 0)
//唤醒后继节点
unparkSuccessor(h);
return true;
}
return false;
}
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);
}
//重入锁时 c!=0 ,c-1
setState(c);
return free;
}
唤醒后继节点
unparkSuccessor(h)
,传入当前head头节点
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
//head 头节点是当前资源占有线程,如果head节点的ws比0小, 则直接将它设为0,因为初始值就是为0
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
// 从尾节点开始向前遍历, 直到找到距离head节点最近的ws<=0的节点
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
//唤醒
if (s != null)
LockSupport.unpark(s.thread);
}