AbstractQueuedSynchronizer源码---JDK1.8
内部类,同步等待队列:
//同步FIFO队列, static final class Node { //共享模式 static final Node SHARED = new Node(); //独占模式 static final Node EXCLUSIVE = null; //等待状态,表示线程被取消 static final int CANCELLED = 1; //等待状态,表示后继线程需要被唤醒 static final int SIGNAL = -1; //等待状态,表示线程正在等待某个条件 static final int CONDITION = -2; //等待状态,表示下一个acquireShared要无条件的往后继节点传递 static final int PROPAGATE = -3; /** * waitStatus,取以下值: * SIGNAL: 当前节点的后继节点已经(或即将)被阻塞,所以如果当前节点释放(控制权),或者被取消时, * 必须唤醒其后继节点。为了避免竞争,请求方法必须首先声明它们需要一个信号,然后(原子的) * 调用请求方法,如果失败,当前线程进入阻塞状态。 * CANCELLED: 表示当前节点已经被取消(由于超时或者中断),节点一旦进入被取消状态,就不会再变成其它状态了。 * 即一个被取消节点的线程永远不会再次被阻塞。 * CONDITION: 表示当前节点处于一个条件队列中。当前节点直到转移时才会被作为一个同步队列的节点使用。转移时 * waitStatus会被设置为0. * PROPAGATE: 表示releaseShared应该被传递到其他节点。该状态值在doReleaseShared过程中进行设置(仅在头节点), * 从而保证持续传递,即使其他操作已经开始。 * 这些状态值之所以用数值来表示,目的是为了方便使用,非负的值意味着节点不需要信号(被唤醒)。 * 所以,一些代码中不需要针对特殊值去做检测,值需要检查符号(正负)即可。 * *针对普通的同步节点,这个waitStatus被初始化为0,针对条件节点(condition nodes),初始化为 * CONDITION(-2)需要通过CAS来修改这个域(如果可能的话,可以使用volatile写操作)。 * */ volatile int waitStatus; /** *指向当前节点的前驱,用于检测等待状态。入队时赋值,出队时置空(for GC)。 *并且,在取消前驱节点的过程中,可以缩短寻找非取消状态节点的过程。 *由于头节点永远不会取消:一个节点只有请求成功才会变成头节点,一个被取消的节点永远不可能请求 * 成功,而且一个线程只能取消自己所在的节点,所以总是存在一个非取消状态节点。 */ volatile Node prev; /** * 指向当前节点的后继节点,释放(控制权)时会唤醒该节点。入队时赋值,跳过取消状态节点时进行调整, * 出队时置空(for GC)。入队操作在完成之前并不会对一个前驱节点的next赋值,所以一个节点的next为null * 并不能说明这个节点在队列尾部。然而,如果next为null,我们可以从尾节点通过前驱节点往前扫描来做双重 * 检测。取消状态节点的next指向自身,这样可以简化isOnSyncQueue的实现。 */ volatile Node next; //使当前节点入队的线程。构造时初始化,用完后清空。 volatile Thread thread; /** * 指向下一个条件等待状态节点或者为SHARED。因为条件队列只有在独占模式下才能访问,所以我们只 * 需要一个普通的链表队列来保存处于等待状态的节点。他们在重新请求的时候会转移到同步队列。所以如果是共享 * 模式,就将这个属性设置为SHARED */ Node nextWaiter; /** * Returns true if node is waiting in shared mode. */ 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; } }
头尾节点和状态:
/** * 同步等待队列的头节点,懒汉式初始化。只能通过setHead方法来改变该属性。 * 注:如果头节点存在,那么它的waitStatus可以保证一定不是CANCELLED。 */ private transient volatile Node head; /** * 同步等待队列的尾节点,懒汉式初始化。只有通过enq()方法来添加一个新的等待节点时才会改变该属性。 */ private transient volatile Node tail; /** * The synchronization state. */ //同步状态 private volatile int state;
独占模式尝试获取请求:
/** * 独占模式下进行请求,忽略中断。实现中至少会调用一次tryAcquire()方法,请求成功后返回。 * 否则,当前线程会排队,可能会重复的阻塞和解除阻塞,调用tryAcquire()方法,直到成功。 */ public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } //指定模式给当前线程创建一个同步队列节点 private Node addWaiter(Node mode) { //根据当前线程和指定的模式创建一个Node。 Node node = new Node(Thread.currentThread(), mode); //尝试快速入队,如果失败再执行enq执行正常入队过程 // Try the fast path of enq; backup to full enq on failure Node pred = tail; //如果不为null,表示入队成功 if (pred != null) { //将当前(线程的)节点的前继节点连接到尾节点 node.prev = pred; //尝试CAS,将当前Node设置为同步等待队列的尾节点。 if (compareAndSetTail(pred, node)) { //如果设置成功,完成链接 pred.next = node; //返回当前节点 return node; } } //为null,说明快速入队失败,进入正常入队过程 enq(node); //返回当前节点。 return node; } //将当前节点插入到同步等待队列。 private Node enq(final Node node) { for (;;) { Node t = tail; //如果t为null,说明同步等待队列为null if (t == null) { // Must initialize //通过CAS来初始化一个头节点 if (compareAndSetHead(new Node())) //头尾链接 tail = head; //不为null } else { //将尾节点设置为当前线程节点的前驱节点 node.prev = t; /** * 节点拼接到同步等待队列总是分为3个步骤: * 1.将prev指向尾节点 * 2.尝试将其设置为尾节点 * 3.将prev节点的next指向自己 * 所以,如果一个节点为尾节点,可以确保prev一定不为null, * 但不能保证prev的next不为null。 */ //CAS,将当前节点设置为同步等待队列的尾节点 if (compareAndSetTail(t, node)) { //CAS成功,完成链表链接。 t.next = node; return t; } } } } /** * 在独占模式下尝试请求(控制权),这个方法应该查看一下对象的状态是否允许在独占模式下请求, * 如果允许再进行请求。 * 该方法总是被请求线程执行,如果方法执行失败,会将当前线程放到同步等待队列中(如果当前线程还不在同步等待队列中), * 直到被其他线程的释放操作唤醒。 * * 该方法在这里并没有实现,留给子类实现。 */ protected boolean tryAcquire(int arg) { throw new UnsupportedOperationException(); } final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { //p,当前节点的前驱节点 final Node p = node.predecessor(); //如果p是头节点,并且获取控制权成功 if (p == head && tryAcquire(arg)) { //将当前节点设置为头节点 setHead(node); p.next = null; // help GC //设置请求标记为成功 failed = false; //返回中断状态false return interrupted; } //到这里,说明请求失败。 //需要判断请求失败后node节点是否应该被阻塞,如果应该被阻塞, //那么阻塞node节点,并检测中断状态 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) //如果有中断,设置中断状态。 interrupted = true; } } finally { if (failed) //如果请求失败,取消请求。 cancelAcquire(node); } } //将node设置为头节点,将node的thread和前继节点置空 private void setHead(Node node) { head = node; node.thread = null; node.prev = null; }
在看看:
/** * 在一个节点请求失败时,检测并更新节点的等待状态,如果当前节点的线程应该被阻塞,那么返回true。 */ private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { //获取前驱节点的等待状态 int ws = pred.waitStatus; //如果是SIGNAL if (ws == Node.SIGNAL) /** * 如果当前节点的前驱节点的状态为SIGNAL,说明当前节点已经声明了需要唤醒,所以可以阻塞当前节点了。直接返回true。 * 一个节点在被阻塞之前需要线程声明一下需要唤醒,也就是将前驱节点的等待状态设置为SIGNAL, */ return true; //大于0,说明前驱节点是取消状态,需要跳过,然后重试 if (ws > 0) { /* * Predecessor was cancelled. Skip over predecessors and * indicate retry. */ do { node.prev = pred = pred.prev; } while (pred.waitStatus > 0); pred.next = node; } else { /** * 到这里了,等待状态一定是0,或者PROPAGATE,将前驱节点的等待状态设置为SIGNAL,来声明需要一个唤醒信号。 * 但是还不需要阻塞。// */ compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; } private final boolean parkAndCheckInterrupt() { //阻塞当前线程 LockSupport.park(this); //线程被唤醒,方法返回当前线程的中断状态,并重置当前线程的中断状态为false。 return Thread.interrupted(); }
再看看cancelAcquire:
private void cancelAcquire(Node node) { // Ignore if node doesn't exist if (node == null) return; //将节点的线程设置为null node.thread = null; // Skip cancelled predecessors-->跳过状态为CALCELLED的前驱节点 Node pred = node.prev; while (pred.waitStatus > 0) node.prev = pred = pred.prev; //predNext节点(node节点前面的第一个非CANCELLED状态节点的后继节点) 是需要断开的节点。 Node predNext = pred.next; node.waitStatus = Node.CANCELLED; // If we are the tail, remove ourselves. //如果当前节点是尾节点,那么删除当前节点(将当前节点的前驱节点设置为尾节点) if (node == tail && compareAndSetTail(node, pred)) { //成功,将前驱节点(已经设置为尾节点)的next清空 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; //如果当前节点不是头节点,那么尝试将当前节点的前驱节点的等待状态设置为SIGNAL,并尝试 //将前驱节点的next指向后继节点,否则唤醒后继节点。 if (pred != head && ((ws = pred.waitStatus) == Node.SIGNAL || (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) && pred.thread != null) { //如果当前节点的前驱节点不是头节点,那么需要给当前节点的后继节点一个SIGNAL标记 //即将当前节点的前驱节点等待状态设置为SIGNAL,然后将其设置为当前节点的后继节点的 //前驱节点 Node next = node.next; if (next != null && next.waitStatus <= 0) compareAndSetNext(pred, predNext, next); } else { //唤醒当前节点的后继节点 unparkSuccessor(node); } //取消节点的next会指向自己 node.next = node; // help GC } } //如果node存在后继节点,唤醒后继节点 private void unparkSuccessor(Node node) { /** *如果node的等待状态为负数(例如:可能需要一个信号),尝试清空等待唤醒的状态(将状态置为0) * 即使设置失败,或者改状态已经被正在等待的线程修改,也没有任何影响。 */ int ws = node.waitStatus; if (ws < 0) //如果当前的状态小于0,尝试设置为0 compareAndSetWaitStatus(node, ws, 0); /** * 需要唤醒的线程在node的后继节点,一般来说就是node的next引用指向的节点。 * 但是如果next指向的节点被取消或者为null,那么就同步等待队列的队尾反向查找 * 离当前节点最近的且状态不是CANCELLED的节点 */ Node s = node.next; 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; } if (s != null) //如果存在(需要唤醒的节点),将该节点的线程唤醒 LockSupport.unpark(s.thread); }
独占模式尝试请求获取控制权,
/** * 独占模式下进行请求,如果当前线程被中断,放弃方法执行(抛异常), * 1.检查当前线程的中断状态,然后至少执行一次tryAcquire, * 2.如果成功,方法返回; * 3.如果失败,当前线程会在同步等待队列中排队,直到方法返回成功或者线程被中断。 */ public final void acquireInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); if (!tryAcquire(arg)) doAcquireInterruptibly(arg); } //acquireQueued类似,但是这里响应中断(抛异常) private void doAcquireInterruptibly(int arg) throws InterruptedException { final Node node = addWaiter(Node.EXCLUSIVE); boolean failed = true; try { for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return; } //通过抛异常,来响应中断 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) throw new InterruptedException(); } } finally { if (failed) cancelAcquire(node); } }
指定时间内尝试请求控制权,
/** * 独占模式下进行请求,如果当前线程被中断,响应中断(抛异常),返回 * 指定时间内尝试请求控制权, */ public final boolean tryAcquireNanos(int arg, long nanosTimeout) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); return tryAcquire(arg) || doAcquireNanos(arg, nanosTimeout); }
共享模式下请求控制权,
/** * 1.调用tryAcquireShared尝试进行控制权请求,如果成功,返回。 * 2.如果请求失败,那么会用当前线程建立一个共享模式的节点,然后将节点 * 放到同步等待队列的尾部,进入循环。 * 3.循环中会判断当前同步等待队列中是否有其他线程,如果没有,再次调用tryAcquireShared * 4.如果请求成功,将当前节点设置为同步等待队列的头节点,同时检查是否需要 * 继续唤醒下一个共享模式的节点,如果需要就继续执行唤醒动作。这里还会想上传递中断 * 状态,然后退出循环。 * 5.如果在同步等待队列中,在当前线程前面有其他线程,或者第3步失败, * 那么首先需要检查当前节点是否已经设置等待唤醒标记,即将非取消状态前驱节点的等待 * 状态设置为SIGNAL。 * 6.如果没有设置等待唤醒标记,进行设置,然后继续循环,进入第三步 * 7.如果已经设置等待唤醒标记,那么阻塞当前线程 * 8.当前线程被唤醒后,设置传递中断标记,然后继续循环,继续第3步。 * 9.最后在循环退出后,要判断请求是否失败,如果失败,当前线程取消请求、 */ /** * 共享模式下请求控制权,忽略中断, */ public final void acquireShared(int arg) { //如果以共享模式尝试请求失败 if (tryAcquireShared(arg) < 0) doAcquireShared(arg); } /** * 在共享模式下尝试请求(控制权),需要先查看一下对象的状态是否允许在共享 * 模式下请求,如果允许在进行请求。 * * 这个方法总是被请求线程执行,如果方法执行失败,会将当前线程放到同步等待队列 * 中(如果当前线程还不在同步等待队列中),直到被其他线程的释放操作唤醒。 * * 留给子类实现 */ protected int tryAcquireShared(int arg) { throw new UnsupportedOperationException(); } private void doAcquireShared(int arg) { //将当前线程以共享模式加入同步等待队列。 final Node node = addWaiter(Node.SHARED); boolean failed = true; try { boolean interrupted = false; //循环 for (;;) { //获取当前节点的前驱p final Node p = node.predecessor(); //如果p是头节点, if (p == head) { //调用tryAcquireShared int r = tryAcquireShared(arg); if (r >= 0) { //到这里,说明tryAcquireShared执行成功, //即在共享模式下获取控制权成功, 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); } } /** * 将node设置为同步等待队列的头节点,并且检测一下node的后继节点是否 * 在共享模式下等待,如果是,并且propagate>0或者之前头节点的等待状态是 * PROPAGATE,唤醒后继节点。 */ private void setHeadAndPropagate(Node node, int propagate) { Node h = head; // Record old head for check below setHead(node); /** * 尝试去唤醒队列中的下一个节点,如果满足下面的条件: * 传递(propagate>0), * 或者h.waitStatus为PROPAGATE,且下一个节点处于共享模式或者为null */ if (propagate > 0 || h == null || h.waitStatus < 0 || (h = head) == null || h.waitStatus < 0) { Node s = node.next; if (s == null || s.isShared()) doReleaseShared(); } } /** * 共享模式下释放控制权,唤醒后继节点并确保传递。 * 注:在独占模式下,释放仅仅意味着,唤醒头结点的后继节点。 */ private void doReleaseShared() { /** * 保证释放动作传递(向同步等待队列尾部),即使没有其他正在进行的请求或释放动作。 * 如果头节点的后继节点需要唤醒,那么执行唤醒动作;如果不需要唤醒,将头节点的等待状态 * 设置为PROPAGATE保证唤醒传递。另外,为了防止过程中有新节点进入(队列), * 这里必须循环,所以,和其他unparkSuccessor方法使用方式不一样的是,如果 * 头结点等待状态设置失败,重新检测。 */ for (;;) { Node h = head; if (h != null && h != tail) { //如果同步等待队列不为空,获取头节点的等待状态。 int ws = h.waitStatus; //如果等待状态是SIGNAL。说明后继节点需要唤醒 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; } }
共享模式下尝试请求,响应中断
//共享模式下尝试请求,响应中断,抛异常 public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); if (tryAcquireShared(arg) < 0) doAcquireSharedInterruptibly(arg); }
共享模式下尝试请求,响应中断,且支持自定义时间,
//共享模式下尝试请求,响应中断,且支持自定义时间 public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); return tryAcquireShared(arg) >= 0 || doAcquireSharedNanos(arg, nanosTimeout); }
独占模式下的释放方法,
/** * 独占模式下的释放方法。 * 如果tryRelease返回true,会唤醒一个或多个线程。 */ public final boolean release(int arg) { if (tryRelease(arg)) { //如果tryRelease成功 Node h = head; //判断同步等待队列里面是否右需要唤醒的线程, if (h != null && h.waitStatus != 0) //如果有,就唤醒 unparkSuccessor(h); return true; } return false; }
共享模式下的释放方法:
/** * 共享模式下的释放方法。 * 如果tryReleaseShared返回true,会唤醒一个或者多个线程 */ public final boolean releaseShared(int arg) { if (tryReleaseShared(arg)) { doReleaseShared(); return true; } return false; } /** * 共享模式下释放控制权,唤醒后继节点并确保传递。 * 注:在独占模式下,释放仅仅意味着,唤醒头结点的后继节点。 */ private void doReleaseShared() { /** * 保证释放动作传递(向同步等待队列尾部),即使没有其他正在进行的请求或释放动作。 * 如果头节点的后继节点需要唤醒,那么执行唤醒动作;如果不需要唤醒,将头节点的等待状态 * 设置为PROPAGATE保证唤醒传递。另外,为了防止过程中有新节点进入(队列), * 这里必须循环,所以,和其他unparkSuccessor方法使用方式不一样的是,如果 * 头结点等待状态设置失败,重新检测。 */ for (;;) { Node h = head; if (h != null && h != tail) { //如果同步等待队列不为空,获取头节点的等待状态。 int ws = h.waitStatus; //如果等待状态是SIGNAL。说明后继节点需要唤醒 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; } }
//返回同步等待队列中第一个线程,没有返回null。 public final Thread getFirstQueuedThread() { // handle only fast path, else relay return (head == tail) ? null : fullGetFirstQueuedThread(); } private Thread fullGetFirstQueuedThread() { /** * 通常情况下,头节点的next指向的就是队列里第一个节点。 * 尝试获取第一个节点的线程,保证读取的一致性:如果线程为null, * 或者第一个节点的前驱节点已经不是头节点,那么说明其他线程正在 * 调用setHead方法。这里尝试获取两次,如果失败,再进行下面的遍历 */ Node h, s; Thread st; if (((h = head) != null && (s = h.next) != null && s.prev == head && (st = s.thread) != null) || ((h = head) != null && (s = h.next) != null && s.prev == head && (st = s.thread) != null)) return st; /** * 头节点的next可能还没有设置,或者已经在setHead后被重置。 * 所以我们必须验证尾节点是否是真的是第一个节点。如果不是, * 从尾节点反向遍历去查找头节点,确保程序退出。 */ Node t = tail; Thread firstThread = null; while (t != null && t != head) { Thread tt = t.thread; if (tt != null) firstThread = tt; t = t.prev; } return firstThread; }
/** * 如果同步等待队列中第一个线程是独占模式,返回true。 * 如果这个方法返回true,并且当前线程正在尝试在共享模式下请求, * 那么可以保证当前线程不是同步等待队列里的第一个线程。 */ final boolean apparentlyFirstQueuedIsExclusive() { Node h, s; return (h = head) != null && (s = h.next) != null && !s.isShared() && s.thread != null; }
看看内部类ConditionObject:(条件等待队列)
await方法:
/** * 1.如果当前线程有中断状态,抛异常,响应中断。 * 2.添加当前线程当条件等待队列。 * 3.释放当前线程对AQS的控制权,并保存释放前AQS的状态。 * 4.进入条件循环,条件为,判断当前线程是否在AQS同步队列中, * 如果不在,那么阻塞当前线程;如果在同步队列中,跳到第7步 * 5.当前线程被(其他线程)唤醒后,要检查等待过程中是否被中断或者 * 取消,如果没有继续循环,开始第4步 * 6.如果是,保存中断状态和模式,然后退出条件循环。 * 7.请求AQS控制权,然后做一些收尾工作,如果被取消,清理一些条件等待队列。 * 然后按照中断模式处理一下中断。 */ //可中断的条件等待方法。 public final void await() throws InterruptedException { //检查线程中断状态,响应中断 if (Thread.interrupted()) throw new InterruptedException(); //如果不是中断状态,将当前线程添加到条件等待队列。 Node node = addConditionWaiter(); //释放当前线程对AQS的控制权,并返回当前AQS中的state的值 int savedState = fullyRelease(node); int interruptMode = 0; //判断当前线程是否在AQS的同步等待队列中 while (!isOnSyncQueue(node)) { //如果不在,阻塞当前线程。 LockSupport.park(this); //其他线程调用相同条件上的signal/sinalAll方法时,会将这个节点 //从条件队列转义到AQS的同步等待队列中。 //被唤醒后需要检查是否在等待过程中被中断 if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) //如果有中断,退出循环 break; } //重新请求AQS的控制权 if (acquireQueued(node, savedState) && interruptMode != THROW_IE) interruptMode = REINTERRUPT; if (node.nextWaiter != null) // clean up if cancelled unlinkCancelledWaiters(); if (interruptMode != 0)//如果发生过中断,在这里处理 reportInterruptAfterWait(interruptMode); } /** * 向同步等待队列中添加一个新的节点 */ private Node addConditionWaiter() { Node t = lastWaiter; // If lastWaiter is cancelled, clean out. if (t != null && t.waitStatus != Node.CONDITION) { unlinkCancelledWaiters(); t = lastWaiter; } //根据当前线程创建一个节点 Node node = new Node(Thread.currentThread(), Node.CONDITION); if (t == null) //到这里,说明node是队列中的第一个节点,那么将firstWaiter指向这个节点 firstWaiter = node; else //如果队列中已经存在其他节点,那么将t的nextWaiter指向该node节点 t.nextWaiter = node; //将lastWaiter指向node节点 lastWaiter = node; return node;//返回node } /** * 移除条件等待队列中的取消状态节点。 * 这个方法一定是在持有锁(拥有AQS控制权)的情况下被调用的(所以不存在竞争)。 * 当等待条件时被(节点的线程)取消,或者当lastWaiter被取消后,条件等待队列中进入了 * 一个新节点时会调用这个方法。这个方法需要避免由于没有signal而引起的 * 垃圾滞留。所以尽管方法内会做一个完全遍历,也只有超时获取或取消时(没有 * signal的情况下)才被调用。方法中会遍历所有节点,切断所有指向垃圾节点的引用, * 而不是一次取消切断一个引用。 */ private void unlinkCancelledWaiters() { //获取条件等待队列中的头节点t Node t = firstWaiter; Node trail = null; while (t != null) { //如果队列中有等待节点。获取头结点的nextWaiter节点next Node next = t.nextWaiter; if (t.waitStatus != Node.CONDITION) { //如果t被取消,将t的nextWaiter清空 t.nextWaiter = null; if (trail == null) //将next设置为头节点(移除之前的取消节点) firstWaiter = next; //否则说明队列前端有未取消的节点,这里链接(移除中间的 // 取消节点) else trail.nextWaiter = next; if (next == null) lastWaiter = trail;//设置尾节点 } else//如果t没被取消,将trail指向t trail = t; t = next; } } /** * 调用release方法,并传入当前的state * 成功,返回之前的state * 失败,抛异常,并取消当前节点 */ final int fullyRelease(Node node) { boolean failed = true; try { int savedState = getState(); if (release(savedState)) { failed = false; return savedState; } else { throw new IllegalMonitorStateException(); } } finally { if (failed) node.waitStatus = Node.CANCELLED; } } /** * 如果一个node最初放在一个条件队列里,而现在正在AQS的同步等待队列里, * 返回true */ final boolean isOnSyncQueue(Node node) { if (node.waitStatus == Node.CONDITION || node.prev == null) return false; //如果有后继节点,说明肯定在AQS同步等待队列里面 if (node.next != null) // If has successor, it must be on queue return true; /** * node.prev不为空并不能说明节点在AQS的同步等待队列里面, * 因为后续的CAS操作可能会失败,这里从尾节点反向遍历。 */ return findNodeFromTail(node); }
signal方法:
/** * 将条件等待队列里面等待时间最长(链表最前面)的线程(如果存在的话), * 移动到AQS同步等待队列里面。 */ public final void signal() { //判断AQS的控制权是否被当前线程以独占的方式持有。 if (!isHeldExclusively()) throw new IllegalMonitorStateException(); Node first = firstWaiter; if (first != null)//判断条件队列里面是否有线程等待, //如果有,执行唤醒操作 doSignal(first); } //唤醒指定节点 private void doSignal(Node first) { do { //移除first if ( (firstWaiter = first.nextWaiter) == null) lastWaiter = null; first.nextWaiter = null; //调用transferForSignal,如果调用失败,且条件等待队列不为空, //继续循环操作 } while (!transferForSignal(first) && (first = firstWaiter) != null); } //将一个节点从条件等待队列转移到同步等待队列,如果成功,返回true。 final boolean transferForSignal(Node node) { /* * If cannot change waitStatus, the node has been cancelled. */ //如果设置等待状态失败,说明节点已经被取消了,返回false。 if (!compareAndSetWaitStatus(node, Node.CONDITION, 0)) return false; //将node加入到AQS同步等待队列中,并返回node的前驱 Node p = enq(node); int ws = p.waitStatus; //如果前驱节点被取消,或者尝试设置前驱节点的状态SIGNAL(表示node需要唤醒) //失败,那么唤醒node节点上的线程。 if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL)) LockSupport.unpark(node.thread); return true; }
signalAll方法:
public final void signalAll() { if (!isHeldExclusively()) throw new IllegalMonitorStateException(); Node first = firstWaiter; if (first != null) doSignalAll(first); } //唤醒所有节点 private void doSignalAll(Node first) { //将条件等待队列的头尾节点置空 lastWaiter = firstWaiter = null; //循环遍历清空所有节点。 do { Node next = first.nextWaiter; first.nextWaiter = null; transferForSignal(first); first = next; } while (first != null); }
知识的学习,要真诚与谦虚才不会有眼无珠,人生苦短,不能浪费时间做无用功。
人生学习最悲哀的不过是,因为无知傲慢错过真正的好东西,又因为无知贪婪在假东西上耗费生命。