一、什么是AQS?
AQS: AbstractQueuedSynchronizer,队列同步器,它是构建锁或者其他同步组件的基础框架,它包含了state变量、加锁线程、等待队列等并发中的核心组件。
二、必备知识点:
该图片来源石衫老师公众号
- AQS使用一个int类型的成员变量state来表示同步状态,state > 0表示获取锁,state == 0表示无锁状态
- AQS使用一个内部变量记录获取锁的当前线程
- AQS使用内部的CLH队列实现竞争线程的等待
- 具体的获取和释放独占锁的逻辑都放在ReentrantLock中自己实现,AQS中负责管理获取或释放独占锁成功失败后需要具体处理的逻辑,默认非公平锁
- AQS定义两种资源共享方式:
- Exclusive,独占,只有一个线程能执行,如ReentrantLock
- Share,共享,多个线程可同时执行,如Semaphore/CountDownLatch
三、ReentrantLock加锁伪代码
伪代码非公平锁加锁流程
ReentrantLock lock = new ReentrantLock();
lock.lock() {
if(CAS拿锁) {
设置currentThread为内部变量
} else {
acquire拿锁
}
}
//加锁
acquire(1) {
if tryAcquire 获取锁失败
addWaiter 使用当前线程信息构建Node,添加节点至等待队列
acquireQueued 在队列中等待的节点有机会尝试获取锁,或者超时/中断
调用线程中断方法
}
//非公平锁
tryAcquire
state == 0 锁空闲
再次CAS获取锁(hasQueuedPredecessors 公平锁这里会判断当前队列是否存在等待线程)
已存在锁 || 获取锁失败
current == getExclusiveOwnerThread() 判读是否是当前线程
是 加可重入锁
return false 不满足以上获取锁失败
四、源码解读
加锁源码解读
先来看一个重要的方法,独占加锁模式下的顶层入口:
public final void acquire(int arg) {
if (!tryAcquire(arg) && //尝试获取获取锁
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) //获取锁失败,addWaiter 将当前线程封装为Node,acquireQueued 返回当前线程在阻塞状态是否中断
selfInterrupt();
}
四个函数:
- 1) 首先,调用使用者重写的 tryAcquire() 方法,若返回true,意味着获取同步状态成功,后面的逻辑不再执行;若返回false,也就是获取同步状态失败,进入2步骤
- 2) 获取同步状态失败,构造独占式同步结点,通过 addWatiter() 方法将此结点添加到同步队列的尾部(此时可能会有多个线程结点试图加入同步队列尾部,需要以线程安全的方式添加)
- 3) acquireQueued() 方法是该结点在队列中尝试获取同步状态,若获取不到,则阻塞结点线程,直到被前驱结点唤醒或者被中断
- 4) 如果线程在等待过程中被中断过,返回true。只有在获取资源后才再进行自我中断selfInterrupt(),将中断补上
构造函数
/**
* Creates an instance of {@code ReentrantLock}. 创建ReentrantLock的实例
* This is equivalent to using {@code ReentrantLock(false)}. 相当于使用ReentrantLock(false)
* 无参构造函数,使用非公平锁
*/
public ReentrantLock() {
sync = new NonfairSync();
}
//有参构造函数,使用公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
lock()方法
public void lock() {
sync.lock();
}
//公平锁实现
final void lock() {
acquire(1);
}
//非公平锁实现
final void lock() {
//插队操作,CAS设置state状态为1,1为加锁状态,0为无锁状态
if (compareAndSetState(0, 1))
//设置当前线程为本地变量,该本地变量初始值为null
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1); //插队失败,再尝试获取锁
}
acquire(1)方法
public final void acquire(int arg) {
if (!tryAcquire(arg) && //尝试获取获取锁
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) //获取锁失败,addWaiter 将当前线程封装为Node,acquireQueued 返回当前线程在阻塞状态是否中断
selfInterrupt();
}
//Fair version of tryAcquire 公平锁
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState(); //获取state状态
if (c == 0) { //0表示无锁状态
if (!hasQueuedPredecessors() && //公平锁会先判断队列中是否存在等待线程
compareAndSetState(0, acquires)) { //不存在,CAS拿锁
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) { //内部变量值是否为当前线程,是则表明可重入加锁
int nextc = c + acquires; //state 值叠加,每次加锁,值累加1
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
//判断队列是否有等待线程
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
//非公平锁
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)) { //这里没有检查等待队列操作,直接CAS拿锁
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;
}
封装当前线程信息构造Node并加入CLH队列
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode); //封装NewNode
// Try the fast path of enq; backup to full enq on failure
Node pred = tail; //tail 尾节点
if (pred != null) { //tail不空代表队列有等待线程
node.prev = pred; //NewNode前驱节点指向tail
if (compareAndSetTail(pred, node)) { //CAS将tail指向NewNode即将NewNode加入队尾
pred.next = node;
return node;
}
}
enq(node); //尾节点为空 or 设置尾节点失败,循环设置尾节点
return node;
}
private Node enq(final Node node) {
for (;;) { //死循环直到成功设置NewNode到tail
Node t = tail; //tail --> NewNode
if (t == null) { // Must initialize //尾节点为空初始化
if (compareAndSetHead(new Node())) //CAS将null更新成NewNode,head --> NewNode
tail = head; // 到这里,head和tail都指向 NewNode
} else {
node.prev = t; //尾节点不为空,NewNode前驱节点指向tail
if (compareAndSetTail(t, node)) { //CAS将NewNode加入尾节点
t.next = node;
return t;
}
}
}
}
acquireQueued()
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor(); //获取NewNode的前置节点
if (p == head && tryAcquire(arg)) { //如果前置节点为head,再尝试加锁
setHead(node); //获取锁成功,设置NeNode为头节点即将自身设置为头结点
p.next = null; // help GC 删除头节点
failed = false;
return interrupted; //不响应中断
}
if (shouldParkAfterFailedAcquire(p, node) && //NewNode前置不为头节点或者拿锁失败,是否需要阻塞线程
parkAndCheckInterrupt()) //使用原语park方法阻塞线程
interrupted = true; //响应中断
}
} finally {
if (failed)
cancelAcquire(node);
}
}
释放锁源码解读
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
if (tryRelease(arg)) { //尝试释放锁
Node h = head;
if (h != null && h.waitStatus != 0) //头节点不空,状态不为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) { //是当前线程,且将状态值减1后,state = 0
free = true;
setExclusiveOwnerThread(null); //设置内部变量为null
}
setState(c);
return free; //返回释放锁成功
}
//唤醒头结点后续节点
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; //获取node节点状态
if (ws < 0) //小于0则更新成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;
if (s == null || s.waitStatus > 0) { //下一节点状态为0或者节点为空,跳过该节点,找到唤醒的目标节点
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);
}
释放锁的逻辑比较简单,state状态值减1,判断释放的锁是否为当前线程,如果是当前线程,state状态设置0,内部保存当前线程的变量置为null,不是当前线程抛异常。
释放锁后,如果等待队列头节点非空,需要唤醒头节点后续节点,下一节点状态为0或者节点为空,跳过该节点,从尾节点向头节点遍历,找到需要唤醒的节点。