我们都知道java提供的锁有两种:Lock和synchronized,其中synchronized是通过jvm实现的,而Lock(eg:ReentrantLock)则是通过AbstractQueuedSynchronizer辅助实现锁功能的。下面我们基于JDK1.8,从源码角度分析AbstractQueuedSynchronizer的实现。
一、AQS简介
对于同步器的设计思想,引用《Java并发编程的艺术》中对同步器的介绍:
同步器是实现锁(也可以是任意同步组件)的关键,在锁的实现中聚合同步器,利用同步器实现锁的语义。可以这样理解二者之间的关系:锁是面向使用者的,它定义了使用者与锁交互的接口(比如可以允许两个线程并行访问),隐藏了实现细节;同步器面向的是锁的实现者,它简化了锁的实现方式,屏蔽了同步状态管理、线程的排队、等待与唤醒等底层操作。锁和同步器很好地隔离了使用者和实现者所需关注的领域。
同步器的设计是基于模板方法模式的,也就是说,使用者需要继承同步器并重写指定的方法,随后将同步器组合在自定义同步组件的实现中,并调用同步器提供的模板方法,而这些模板方法将会调用使用者重写的方法。
同步器提供的模板方法基本上分为3类:独占式获取与释放同步状态、共享式获取与释放同步状态和查询同步队列中的等待线程情况。自定义同步组件将使用同步器提供的模板方法来实现自己的同步语义。
下面会将从以下方面:“独占式获取与释放同步状态”、“共享式获取与释放同步状态”、“同步队列的实现”以及“查询同步队列中的等待线程情况” 分析AQS源码。
二、AQS基本属性和等待队列介绍
-
AQS的类属性、实例属性
//等待队列的首节点(懒加载,用到同步队列再初始化)
private transient volatile Node head; //等待队列的尾节点(懒加载,用到同步队列再初始化) private transient volatile Node tail; //同步状态 private volatile int state; //如果超时时间小于此阈值,不阻塞线程,让其自旋 static final long spinForTimeoutThreshold = 1000L; private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long stateOffset; //state变量在内存地址中的偏移量 private static final long headOffset; //head变量在内存地址中的偏移量 private static final long tailOffset; //tail变量在内存地址中的偏移量 private static final long waitStatusOffset; //Node对象的waitStatus变量在内存地址中的偏移量 private static final long nextOffset; //Node对象的next变量在内存地址中的偏移量
static { try { stateOffset = unsafe.objectFieldOffset (AbstractQueuedSynchronizer.class.getDeclaredField("state")); headOffset = unsafe.objectFieldOffset (AbstractQueuedSynchronizer.class.getDeclaredField("head")); tailOffset = unsafe.objectFieldOffset (AbstractQueuedSynchronizer.class.getDeclaredField("tail")); waitStatusOffset = unsafe.objectFieldOffset (Node.class.getDeclaredField("waitStatus")); nextOffset = unsafe.objectFieldOffset (Node.class.getDeclaredField("next")); } catch (Exception ex) { throw new Error(ex); } }
同步队列/等待队列的节点:Node
特别说明以下节点状态waitStatus,节点创建后,默认值是0,然后被添加到同步队列。而且这个时候需要把它的前置节点的状态改成“SIGNAL”。目的是在于告诉前置节点,你后面还有节点在阻塞,你释放锁后记得唤醒我。
static final class Node { //共享锁的标记(用在下方的nextWaiter) static final Node SHARED = new Node(); //独占锁的标记(用在下方的nextWaiter) static final Node EXCLUSIVE = null; //waitStatus的值,表示在同步队列中的节点超时或者中断,需要取消等待,进入该状态后将不会再变化 static final int CANCELLED = 1; //waitStatus的值,指示该节点的后续节点的线程处于等待状态。在当前节点释放了同步状态或者被取消后,记得通知后继节点,使后继节点得以运行 static final int SIGNAL = -1; //waitStatus的值,标志当前节点在condition的等待队列中,在调用了Condition的signal后将会从等待队列移到同步队列 static final int CONDITION = -2; //waitStatus的值,表示下一次共享式同步状态获取将会无条件被传播下去。(首节点是共享锁,如果接下来的节点是共享过去,那么会把传递下去,这个是允许传递的状态) static final int PROPAGATE = -3;
//当前节点线程的状态,默认是0. volatile int waitStatus; //链表的前驱节点 volatile Node prev; //链表的后驱节点 volatile Node next; //节点的线程 volatile Thread thread; //用于Condition等待队列的后继节点(等待队列是FIFO的单链表,所以一个指针就可以)。还有一个作用:如果当前节点是共享的,那个该字段是一个SHARED常量 Node nextWaiter;
//结点是否在共享模式下等待 final boolean isShared() { return nextWaiter == SHARED; } final Node predecessor() throws NullPointerException { Node p = prev; if (p == null) throw new NullPointerException(); else return p; } Node() { // Used to establish initial head or SHARED marker } Node(Thread thread, Node mode) { // Used by addWaiter this.nextWaiter = mode; this.thread = thread; } Node(Thread thread, int waitStatus) { // Used by Condition this.waitStatus = waitStatus; this.thread = thread; } }
同步队列的基本结构
-
继承AQS需要重写的方法
继承AQS后,需要我们重写一些方法,以达到自己的想要的同步队列效果。
//独占式获取同步状态
protected boolean tryAcquire(int arg) { throw new UnsupportedOperationException(); } //独占式释放同步状态 protected boolean tryRelease(int arg) { throw new UnsupportedOperationException(); } //共享式获取同步状态 protected int tryAcquireShared(int arg) { throw new UnsupportedOperationException(); } //共享式释放同步状态 protected boolean tryReleaseShared(int arg) { throw new UnsupportedOperationException(); } //当前同步器是否在独占模式下被线程占用,一般该方法用于表示是否被当前线程占用 protected boolean isHeldExclusively() { throw new UnsupportedOperationException(); }
三、AQS独占式同步状态获取与释放
-
独占式获取同步状态
//1、尝试获取独占锁 //2、获取独占锁失败,则添加一个独占锁节点(Node.EXCLUSIVE)到同步队列,然后通过acquireQueued阻塞获取锁 //3、如果acquireQueued等待的时候,发生中断,则返回true public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } //添加节点到同步队列尾部 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) { //不为null,说明tail和head已经初始化(注意它们是懒加载) node.prev = pred; //当前线程的节点加到队列尾部 if (compareAndSetTail(pred, node)) { pred.next = node; //添加成功,返回 return 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; } } } } //线程自旋获取独占锁 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)) { //1、当前节点晋升为首节点(移除了head指向p的引用) setHead(node); //2、移除原来的首节点指向当前节点的引用,加速原来的首节点的GC p.next = null; // help GC failed = false; return interrupted; } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } } //根据前一个节点pred的状态,来判断当前节点对应的线程是否应该被阻塞 private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { int ws = pred.waitStatus; if (ws == Node.SIGNAL) //如果前置节点的状态是SIGNAL,那么当前节点线程会被挂起 return true; if (ws > 0) { //线程被取消的情况,把当前节点的prev指针重新指向前面第一个非取消的节点 do { node.prev = pred = pred.prev; } while (pred.waitStatus > 0); pred.next = node; } else { //剩下0和PROPAGATE的情况,这个时候把前置节点状态设置成SIGNAL,等待下一次循环挂起当前线程 //每次新增尾节点,都需要把它的前置节点状态设置成“SIGNAL”,目的是告诉前置节点,你到时释放锁记得唤醒我(因为线程已经被挂起,无法竞争锁,不知道什么时候释放) compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; } //取消独占锁的获取:(1)把节点从同步队列移除(2)节点线程置null(3)节点状态置为CANCELLED private void cancelAcquire(Node node) { // Ignore if node doesn't exist if (node == null) return; node.thread = null; // 跳过所有已经被取消的前置节点(因为当前节点需要出队,当前节点的前置节点需要改为指向当前节点的后继节点,所以前置不能为已取消的) Node pred = node.prev; while (pred.waitStatus > 0) node.prev = pred = pred.prev; Node predNext = pred.next; //当前节点置为取消 node.waitStatus = Node.CANCELLED; // 如果当前节点是尾部,移除当前节点 if (node == tail && compareAndSetTail(node, pred)) { compareAndSetNext(pred, predNext, null); } else { // If successor needs signal, try to set pred's next-link // so it will get one. Otherwise wake it up to propagate. int ws;
//非首节点 if (pred != head && ((ws = pred.waitStatus) == Node.SIGNAL || (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) && pred.thread != null) { Node next = node.next; if (next != null && next.waitStatus <= 0)
//将当前节点的前置和后置节点相连 compareAndSetNext(pred, predNext, next); } else {
//当前节点已经是首节点,那么唤醒当前节点的后继节点 unparkSuccessor(node); } //将要取消节点的下一节点设置为自身,加快gc node.next = node; // help GC } }
-
独占式释放同步状态
public final boolean release(int arg) {
//先调用tryRelease尝试释放锁,此方法由子类实现 if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; } //释放锁后,需要唤醒后继节点 private void unparkSuccessor(Node node) { //如果状态小于0,则把它设置成0 int ws = node.waitStatus; if (ws < 0) compareAndSetWaitStatus(node, ws, 0); //如果后继节点为null或者已经取消 Node s = node.next; if (s == null || s.waitStatus > 0) { s = null;
//从同步队列尾部开始,往前查找不为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); }
四、AQS共享式同步状态获取与释放
-
共享式获取同步状态
public final void acquireShared(int arg) { //尝试共享式获取同步状态,这个方法由子类实现(tryAcquireShared小于0,说明获取锁失败。) if (tryAcquireShared(arg) < 0) //死循环获取共享锁(期间会被挂起) doAcquireShared(arg); } /** * Acquires in shared uninterruptible mode. * @param arg the acquire argument */ private void doAcquireShared(int arg) { //创建一个共享式的节点 final Node node = addWaiter(Node.SHARED); boolean failed = true; try { boolean interrupted = false; for (;;) { //获取当前节点的前节点 final Node p = node.predecessor(); if (p == head) { //前节点是首节点的话,尝试获取共享锁 int r = tryAcquireShared(arg); if (r >= 0) { //获取共享锁成功,设置首节点为自己,并且尝试把共享锁传递下去 setHeadAndPropagate(node, r); p.next = null; // help GC if (interrupted) selfInterrupt(); failed = false; return; } } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } } private void setHeadAndPropagate(Node node, int propagate) { Node h = head; // Record old head for check below setHead(node); // if (propagate > 0 || h == null || h.waitStatus < 0 || (h = head) == null || h.waitStatus < 0) { Node s = node.next;
// 如果节点s是空或者共享模式节点,那么就要唤醒共享锁上等待的线程 if (s == null || s.isShared()) doReleaseShared(); } }
-
共享式释放同步状态
public final boolean releaseShared(int arg) { if (tryReleaseShared(arg)) { doReleaseShared(); return true; } return false; } /** * Release action for shared mode -- signals successor and ensures * propagation. (Note: For exclusive mode, release just amounts * to calling unparkSuccessor of head if it needs signal.) */ private void doReleaseShared() { // for (;;) { Node h = head; if (h != null && h != tail) { int ws = h.waitStatus; if (ws == Node.SIGNAL) { if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) continue; // loop to recheck cases unparkSuccessor(h); }
//如果首节点状态为0,就设置状态为:PROPAGATE else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) continue; // loop on failed CAS } if (h == head) // loop if head changed break; }