AQS详解
AQS 全程AbstractQueuedLongSynchronizer 抽象队列同步器
用来构建锁或者其他同步器组件的重量级基础框架及整个JUC体系的基石,通过内置的FIFO队列来完成资源获取线程的排队工作,并通过一个int state变量来表示持有锁的状态
- 抢到资源的线程直接继续执行,抢占不到资源的线程必然涉及到一个队列等候机制
- 如果共享资源被占用,就需要阻塞等待唤醒的机制保证锁分配
- 队列机制在AQS中主要是用CLH队列的变体实现,将暂时获取不到锁的线程加入队列中
- CLH队列将请求共享资源的线程封装成队列的节点 Node,通过CAS,自旋以及LockSupport的park方法,维护state变量的状态,使得并发达到同步的效果
AQS使用一个volatile的int类型成员变量来表示同步状态,通过内置的FIFO队列来完成资源获取队列,队列中每条抢占资源线程封装成一个Node节点来实现锁分配,通过CAS完成对state的修改
AQS底层本质上是通过volatile修改的state变量+FIFO的队列+CAS来完成.
/**
* The synchronization state. AQS上的state状态 0表示没有线程占用 >1表示已经被其他线程占用
*/
private volatile long state;
static final class Node {
// 共享模式
static final Node SHARED = new Node();
// 排他锁模式
static final Node EXCLUSIVE = null;
// waitStatus中的可用值之一 表示线程被取消
static final int CANCELLED = 1;
// 同上 当前节点线程锁释放之后,后继线程需要被unpark
static final int SIGNAL = -1;
// 节点在等待队列中wait,当其他线程对condition调用了singal,节点从等待队列加入到同步队列中
static final int CONDITION = -2;
// 共享模式下 直接传播下去
static final int PROPAGATE = -3;
volatile Node prev;
volatile Node next;
volatile Thread thread;
Node nextWaiter;
private transient volatile Node head;
private transient volatile Node tail;
private volatile long state;
// 表示相当节点的等待状态的变量
volatile int waitStatus;
}
AQS底层使用LookSupport.park和unpark来进行排队,Lock接口的实现类基本都通过聚合了一个AQS的子类来完成堆线程的访问控制
如ReentrantLock中有静态内部类Sync实现了AQS
abstract static class Sync extends AbstractQueuedSynchronizer
他又有FairSync 和 NonfairSync 子类来完成对公平锁和非公平锁的实现
源码debug AQS底层
-
ReentrantLock#lock方法底层调用了内部类Sync的lock方法Sync的lock方法调用了公平FairSync或者非公平锁NonFairSync的lock方法
-
第一个线程进来先进行CAS判断修改state的值 将0改为1
final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); }
-
第一个线程CAS成功进入setExclusiveOwnerThread(Thread.currentThread())传入当前线程设置排他线程 本质方法设置了一个AQS父类AbstractOwnableSynchronizer的exclusiveOwnerThread属性,此时lock方法结束
protected final void setExclusiveOwnerThread(Thread thread) { exclusiveOwnerThread = thread; }
-
第二个线程进来之后因为CAS 修改state的时候失败 进入acquire(1)方法
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
-
acquire方法首先进行两个判断分别是tryAcquire(arg)和acquireQueued(addWaiter(Node.EXCLUSIVE), arg) 此时arg 参数是1
-
先进入tryAcquire方法,此时父类方法直接抛出了一个异常,必须子类去实现
protected boolean tryAcquire(int arg) { throw new UnsupportedOperationException(); }
-
以NonFairSync为例实现了tryAcquire方法,底层又调用了nonfairTryAcquire方法acquires值还是1
protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); }
-
进入nonfairTryAcquire方法,进一步分析 尝试获取锁
final boolean nonfairTryAcquire(int acquires) { // 获取当前线程 final Thread current = Thread.currentThread(); // 获取最新的AQS的state属性,因为可能出现刚好执行这块代码的时候,第一个线程已经释放了锁 int c = getState(); if (c == 0) { // 如果前一个线程已经释放了锁调用CAS修改state状态 改为1,本质跟lock方法第一次进入执行的一样 然后返回true if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } // 继续判断如果当前线程跟AQS父类排他线程的exclusiveOwnerThread属性一样说明还是该线程进行加锁,递归锁的体现 此时state+1 然后重新设置回state属性 返回true else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } // 尝试获取锁失败 返回false return false; }
-
根据上面尝试获取tryAquire返回,如果上一个线程还没释放锁最终tryAquire会返回false取反为true继续进入acquireQueued(addWaiter(Node.EXCLUSIVE), arg))逻辑, 在进入addWaiter 方法查看addWaiter方法实现了核心的队列逻辑
public final void acquire(int arg) { // tryAcquire获取锁失败返回false取反进入下一个逻辑 if (!tryAcquire(arg) && // acquireQueued方法之前先执行addWaiter方法,传入Node的排他属性 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
-
addWaiter方法追踪,第一个进入排队队列的线程,此时队列还没初始化head和tail都是null会进入enq 方法进行队列的初始化
private Node addWaiter(Node mode) { // 首先将当前线程封装成一个Node节点,传入默认为排他模式 Node node = new Node(Thread.currentThread(), mode); // 此时将Node的tail尾结点提出来 Node pred = tail; // 第一个排队的线程此时tail和head的都是null不进去if逻辑 if (pred != null) { node.prev = pred; if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } // 第一个排队线程进去进行队列的初始化enq(node)方法 enq(node); return node; }
-
追踪enq(node)方法,为了初始化头节点,上来直接一个死循环,判断当前AQS的等待队列是不是为空,如果是空new一个哑结点Node作为头节点,再进入循环,将封装了线程的Node挂在哑结点后面
private Node enq(final Node node) { for (;;) { // 判断队列的尾结点是不是null Node t = tail; if (t == null) { // Must initialize // 第一个排队线程tail为null,将队列的head和tail都设置成一个新new的node节点 // 注意!!这是新new的一个哑结点并不是真实的封装了线程的node if (compareAndSetHead(new Node())) // 将tail和 head 都指向这个新new的哑结点 // 注意此时for循环并没有返回继续再进入for逻辑 tail = head; } else { // 根据上次判断逻辑t已经不是null了 而是新new出来的哑结点 // 将当前封装了Thread的node节点的前指针指向哑结点 node.prev = t; // 进行CAS操作 将t也就是哑结点改成node 设置到尾部 if (compareAndSetTail(t, node)) { // t.next = node 将哑结点的next指针指向node t.next = node; // 返回哑结点 // 此时FIFO队列变成了 // head tail // | | // 哑结点 -> node -> null // 此时addWaiter方法返回 return t; } } } }
-
继续返回acquire方法 准备进acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
public final void acquire(int arg) { if (!tryAcquire(arg) && // addWaiter返回的是封装了Thread的Node节点 arg=1 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
-
分析acquireQueued方法,此处调用了LockSupport的park方法将线程挂起,也体现了自旋锁的思想
// node是封装了排队Thread的node arg=1 final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; // 死循环 for (;;) { // 获取node的上一个节点 此处是哑结点 final Node p = node.predecessor(); // p==head 此时哑结点就是head true // 仅需tryAcquire尝试获取锁 if (p == head && tryAcquire(arg)) { // 如果获取成功 将头节点改成当前节点 setHead(node); // 前驱节点的next指针断掉 让GC可以回收 p.next = null; // help GC failed = false; // 获取到锁成功 返回interrupted为false退出循环 return interrupted; } // 当前节点的前驱节点不是head头节点并且尝试获取锁失败进入下面逻辑 // 此处的逻辑体现了LockSupport的加锁逻辑 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } }
-
首先进去shouldParkAfterFailedAcquire(p, node)进行判断 p为前驱节点, node为当前node,这个步骤结束之后哑结点的waitStatus从0改成了-1,返回了false
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { // 第一次进来哑结点的waitStatus没有赋值为默认值0 int ws = pred.waitStatus; // static final int SIGNAL = -1; // 第一次ws不等于-1不进入这个分支 if (ws == Node.SIGNAL) // 如果等于-1 说明该node已经准备被唤醒了 return true; // ws=0不进入 if (ws > 0) { do { node.prev = pred = pred.prev; } while (pred.waitStatus > 0); pred.next = node; } else { // 将前驱节点的waitStatus的状态CAS从0改成-1 compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; }
-
shouldParkAfterFailedAcquire返回了false进入packAndCheckInterrupt方法
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true;
-
进入parkAndCheckInterrupt方法本质就调用LockSupport的park方法将线程阻塞
private final boolean parkAndCheckInterrupt() { LockSupport.park(this); return Thread.interrupted(); }
-
经过上述的步骤第一个线程B已经进入了AQS的等待队列
AQS队列的状态为
head tail
哑结点 -> Node(Thread_B) -> null
哑结点的waitStatus=-1 Node(Thread_A) 的waitStatus=0
第三个线程C进入时,此时ThreadA还是执行 ThreadB已经进入等待队列此时的源码debug
-
还是从lock方法出发 还是以非公平锁为例子,CAS state失败进去 acquire
final void lock() { // state不是0 CAS失败进去acuire if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); }
-
进去acquire方法,此时tryAcquire还是失败,进入addWaiter,注意!!此时的AQS的队列已经变成了哑结点->Node(ThreadB)
public final void acquire(int arg) { // tryAcquire获取锁失败 返回 false取反继续进入addWaiter方法 if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
-
addWaiter方法 此时获取tail节点已经不是null而是Node(Thread_B)
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(ThreadC)追加到队列尾部 返回 node.prev = pred; if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } enq(node); return node; }
-
addWaiter方法返回将Node(ThreadC)追加到队列进去acquireQueued方法
final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { // 此时p为 Node(ThreadB) final Node p = node.predecessor(); // p!=head 此时head还是哑结点 if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return interrupted; } // 此时逻辑跟之前一样 将Node(ThreadB)的waitState改为-1 // 然后调用LockSupport将线程挂起 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } }
下面开始调用unlock解锁debug
-
底层调用了AQS的release方法 首先进去tryRelease方法尝试释放锁 进入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; }
-
进入tryRelease
protected final boolean tryRelease(int releases) { // state = 1 -realse 1 = 0 int c = getState() - releases; // 判断当前线程是不是 AQS父类的设置进去的排他线程 if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { free = true; // 设置排他线程为null setExclusiveOwnerThread(null); } // 将state改成0 setState(c); // 返回true return free; }
-
返回release方法继续执行,判断头节点是否为空和waitStatus的状态
public final boolean release(int arg) { if (tryRelease(arg)) { // 获取头节点 此时为哑结点 Node h = head; // 此时头节点部位空 且 waitStatus为-1 进入unparkSuccessor if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; }
-
进去unparkSuccessor方法
private void unparkSuccessor(Node node) { // 此时的node是头节点 头节点的waitStatus = -1 int ws = node.waitStatus; // 执行CAS将 ws从-1改成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; // 此时头节点后继节点不为null不进if 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; } // 调用LockSupport 将后继节点的Thread唤醒 if (s != null) LockSupport.unpark(s.thread); }
-
此时ThreadB已经被唤醒… 注意!!!此时要调回到当时被park的地方继续往下执行,就是lock方法中的代码!!!此时需要对任务等待队列的出队操作,此时调回到AQS的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 因为realse释放了继续往下执行最终进入finally 方法进行队列的出队 parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } }
-
继续进去for循环
for (;;) { final Node p = node.predecessor(); // 此时p是哑结点 进入tryAcquire继续查询获取锁 if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return interrupted; } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; }
-
进入nonfairTryAcquire方法 CAS成功将当前排他线程改成ThreadB返回for循环继续执行
final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); // 此时的state因为ThreadA已经释放了 state=0 设置当前排他线程为ThreadB // 返回True获取锁成功 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; }
-
for循环继续执行,将Node(ThreadB)变成新的头节点(哑结点)
for (;;) { final Node p = node.predecessor(); // 此时p是哑结点 进入tryAcquire继续查询获取锁 if (p == head && tryAcquire(arg)) { // 将当前头节点改成ThreadB // 将当前Head的Thread改成null AQS 的head指针指向该节点当前指针的pre节点指向null // 相当于此时Node(ThreadB)变成了新的哑结点 setHead(node); // 之前的头节点next指针断开与GCRoots的关联让GC可以回收 p.next = null; // help GC failed = false; return interrupted; } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } // setHead方法 private void setHead(Node node) { head = node; node.thread = null; node.prev = null; }
-
此时realse工作完成