【JUC源码解析】AQS

简介

AQS,也即AbstractQueuedSynchronizer,抽象队列同步器,提供了一个框架,可以依赖它实现阻塞锁和相关同步器。有两种类型,独占式(Exclusive)和共享式(Share)。

 

概述

同步器,维护了一个共享状态(state)和一个同步队列(链表)。

共享状态,表示共享资源的状态;初始时为0,表示未锁定,当有一个线程成功抢占此资源时,状态加1,释放资源时,状态减1;通过CAS改变state,一般需要子类实现具体的逻辑。

同步队列(链表,Node),当一个线程抢占资源失败时,会为此线程创建一个Node,并添加到链表尾部。链表里只有排在前头的结点对应的线程才有资格竞争资源,成功则获得锁,访问资源结束后,释放锁,结点也从链表中移除,下次来竞争资源时,会重新为其创建结点。

 

结点

Node

 1     static final class Node {
 2         static final Node SHARED = new Node(); // 标记一个结点(对应的线程)在共享模式下等待
 3         static final Node EXCLUSIVE = null; // 标记一个结点(对应的线程)在独占模式下等待
 4         static final int CANCELLED = 1; // waitStatus的值,表示该结点(对应的线程)已被取消
 5         static final int SIGNAL = -1; // waitStatus的值,表示后继结点(对应的线程)需要被唤醒
 6         static final int CONDITION = -2; // waitStatus的值,表示该结点(对应的线程)在等待某一条件
 7         static final int PROPAGATE = -3; // waitStatus的值,表示有资源可用,新head结点需要继续唤醒后继结点(共享模式下,多线程并发释放资源,而head唤醒其后继结点后,需要把多出来的资源留给后面的结点;设置新的head结点时,会继续唤醒其后继结点)
 8         volatile int waitStatus; // 等待状态,取值范围,-3,-2,-1,0,1
 9         volatile Node prev; // 前驱结点
10         volatile Node next; // 后继结点
11         volatile Thread thread; // 结点对应的线程
12         Node nextWaiter; // 等待队列里下一个等待条件的结点
13 
14         final boolean isShared() { // 判断是否为共享模式
15             return nextWaiter == SHARED;
16         }
17 
18         final Node predecessor() throws NullPointerException { // 前驱结点
19             Node p = prev;
20             if (p == null)
21                 throw new NullPointerException();
22             else
23                 return p;
24         }
25 
26         Node() { // 初始化head或share标记结点
27         }
28 
29         Node(Thread thread, Node mode) { // 同步队列
30             this.nextWaiter = mode;
31             this.thread = thread;
32         }
33 
34         Node(Thread thread, int waitStatus) { // 等待队列
35             this.waitStatus = waitStatus;
36             this.thread = thread;
37         }
38     }

 

属性

1     private transient volatile Node head; // 指向同步队列(wait queue)的头结点
2     private transient volatile Node tail; // 指向同步队列(wait queue)的尾节点
3     private volatile int state; // 同步状态

 

独占模式

获取资源入口

acquire(int)

1     public final void acquire(int arg) { // 入口
2         // 竞争资源成功,直接返回;否则,入队等待,司机而动
3         if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) 
4             selfInterrupt(); // 补上中断
5     }

 

尝试获取资源

tryAcquire(int)

1     protected boolean tryAcquire(int arg) {
2         throw new UnsupportedOperationException(); // 留给子类实现
3     }

 

快速添加结点

addWaiter(Node)

 1     private Node addWaiter(Node mode) {
 2         Node node = new Node(Thread.currentThread(), mode); // 为当前线程创建结点
 3         Node pred = tail;
 4         if (pred != null) { // 队列不为空,尝试快速添加结点
 5             node.prev = pred;
 6             if (compareAndSetTail(pred, node)) { // 设置node为tail结点
 7                 pred.next = node; // 老tail结点的后继指向新tail结点
 8                 return node; // 返回老tail结点,注意,是老的tail结点
 9             }
10         }
11         enq(node); // 否则,自旋添加结点
12         return node;
13     }

 快速通道,当队列不为空时,将结点添加到队尾,CAS操作,成功,则返回老的末尾结点。如下图所示。如果失败,则进入自旋添加结点通道。

 

自旋添加结点

enq(Node)

 1     private Node enq(final Node node) {
 2         for (;;) { // 自旋
 3             Node t = tail;
 4             if (t == null) { // 队列为空
 5                 if (compareAndSetHead(new Node())) // 创建一个标记结点,head指向它
 6                     tail = head; // tail也指向此结点
 7             } else {
 8                 node.prev = t; // node的前驱指向tail结点
 9                 if (compareAndSetTail(t, node)) { // 设置node为tail结点
10                     t.next = node; // 老tail结点的后继指向新tail结点
11                     return t; // 返回老tail结点,注意,是老的tail结点
12                 }
13             }
14         }
15     }

自旋添加结点,如果队列为空,创建一个标记结点,head和tail都指向它,否则,设置node结点为新的tail结点,CAS操作,失败重试,直至成功。 见下图。

队列为空,添加标记结点,好处是,第一个入队的线程结点和后续结点是一样的逻辑,无需特别处理。

 

 

入队等待,伺机而动

acquireQueued(Node, int)

 1     final boolean acquireQueued(final Node node, int arg) {
 2         boolean failed = true; // 标记是否成功
 3         try {
 4             boolean interrupted = false; // 记录中断
 5             for (;;) { // 自旋
 6                 final Node p = node.predecessor(); // 前驱
 7                 if (p == head && tryAcquire(arg)) { // 如果前驱是head结点,表示自己可以竞争资源了(等待中被唤醒或中断)
 8                     setHead(node); // 成功后,将自己设置为head结点
 9                     p.next = null; // 老的head结点出队
10                     failed = false; // 成功
11                     return interrupted; // 返回
12                 }
13                 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) // 寻找安全停靠点,检查中断
14                     interrupted = true;
15             }
16         } finally {
17             if (failed)
18                 cancelAcquire(node); // 失败则取消
19         }
20     }

入队等待。如果当前结点的前驱是head结点,则尝试获取资源,成功则将此结点设置为head结点,老的head结点出队,返回。否则,寻找安全停靠点,并检查中断标记,如果未找到停靠点或被唤醒,则继续尝试竞争资源。线程总是在安全停靠点处打盹儿(挂起),或是在竞争资源的路上。 

 

寻找安全停靠点

shouldParkAfterFailedAcquire(Node, Node)

 1     private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
 2         int ws = pred.waitStatus; // 查看前驱的等待状态
 3         if (ws == Node.SIGNAL) // 释放锁后会唤醒自己
 4             return true; // 可以在此停靠(阻塞)
 5         if (ws > 0) { // 如果前驱已取消
 6             do { // 一直往前找
 7                 node.prev = pred = pred.prev; // 将自己的前驱往前移
 8             } while (pred.waitStatus > 0); // 直到安全停靠点(没有取消的结点)
 9             pred.next = node; // 并将自己设置为前驱的后继结点
10         } else {
11             compareAndSetWaitStatus(pred, ws, Node.SIGNAL); // 与前驱约定好,释放锁后唤醒自己
12         }
13         return false; // 暂时不能停靠,继续尝试获取锁
14     }

寻找安全停靠点。所谓安全停靠点,指的是,当前结点的前驱结点waitStatus = -1(SIGNAL)。否则,检查其是否是有效的,如果无效(已经取消)则往前找,直到找到第一个有效结点,并设置为自己的前驱结点,也把自己设置为它的后继结点(等于把无效结点截掉了);然后将前驱结点的waitStatus设置为-1,并继续竞争资源,暂时不能停靠。如下图。

 

停靠并检查中断

parkAndCheckInterrupt()

1     private final boolean parkAndCheckInterrupt() {
2         LockSupport.park(this); // 线程挂起
3         // 检查中断,并清除中断标记,如果不清除,下次挂起时,会立刻响应中断,所以要清除;最后再把中断补上
4         return Thread.interrupted();
5     }

线程挂起,被唤醒后,需要检查中断标记,同时清除中断标记,是为了下次挂起时,被上次的中断标记立刻中断,从而再也无法被挂起(因为一直有中断标记,一旦被挂起,就立刻响应中断)

 

释放资源入口

release(int)

1     public final boolean release(int arg) {
2         if (tryRelease(arg)) { // 成功释放
3             Node h = head; // head结点
4             if (h != null && h.waitStatus != 0) // waitStatus等于0,说明后面没有要释放的线程结点
5                 unparkSuccessor(h); // 唤醒等待队列里下一个结点对应的线程
6             return true; // 成功
7         }
8         return false; // 失败
9     }

独占式释放资源,这里肯定是单线程,无需考虑并发。head结点存在,并且waitStatus不为0(肯定也不为1,取消),则唤醒等待队列里下一个结点对应的线程。

 

释放资源

tryRelease(int)

1     protected boolean tryRelease(int arg) {
2         throw new UnsupportedOperationException(); // 留给子类实现
3     }

 

通知后继结点

unparkSuccessor(Node)

 1     private void unparkSuccessor(Node node) {
 2         int ws = node.waitStatus;
 3         if (ws < 0)
 4             compareAndSetWaitStatus(node, ws, 0); // waitStatus置为0
 5         Node s = node.next; // 后继结点
 6         if (s == null || s.waitStatus > 0) { // 如果后继结点不存在,或者已取消
 7             s = null;
 8             // 从tail结点开始,往前搜索,直至第一次遇见取消的结点,并将改取消结点的后继结点作为待唤醒的结点
 9             for (Node t = tail; t != null && t != node; t = t.prev)
10                 if (t.waitStatus <= 0)
11                     s = t;
12         }
13         if (s != null) // 如果后继节点存在,则唤醒其所对应的线程
14             LockSupport.unpark(s.thread);
15     }

通知后继结点。当前结点的waitStatus设置为0,不成功也没关系,可能被等待线程改变了(寻找安全停靠点的那个家伙);检查当前线程的后继结点,如果不存在(可能取消了),就从tail结点开始往前找,直到第一次遇见取消的结点,然后将这个取消结点的后继结点作为当前线程要唤醒的后继结点;最后,如果找到了要唤醒的结点,则唤醒此结点对应的线程。

 

共享模式

获取资源入口

acquireShared(int)

1     public final void acquireShared(int arg) {
2         if (tryAcquireShared(arg) < 0) // 尝试获取资源
3             doAcquireShared(arg); // 入队等待
4     }

 

尝试获取资源

tryAcquireShared(int)

1     protected int tryAcquireShared(int arg) {
2         throw new UnsupportedOperationException(); // 留给子类实现
3     }

 

 入队等待

doAcquireShared(int)

 1     private void doAcquireShared(int arg) {
 2         final Node node = addWaiter(Node.SHARED); // 入队
 3         boolean failed = true; // 记录是否成功
 4         try {
 5             boolean interrupted = false; // 记录中断
 6             for (;;) { // 自旋
 7                 final Node p = node.predecessor(); // 前驱结点
 8                 if (p == head) { // 在head后面可以竞争资源
 9                     int r = tryAcquireShared(arg); // 尝试获取资源
10                     if (r >= 0) { // 获取成功
11                         setHeadAndPropagate(node, r); // 设置head指向当前结点,如果剩余资源,通知后面的结点
12                         p.next = null; // help GC
13                         if (interrupted) // 补上中断
14                             selfInterrupt();
15                         failed = false; // 成功
16                         return;
17                     }
18                 }
19                 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) // 寻找安全停靠点,检查中断
20                     interrupted = true;
21             }
22         } finally {
23             if (failed)
24                 cancelAcquire(node); // 失败则取消
25         }
26     }

共享模式下,入队等待。首先尝试获取资源,会返回一个整数,大于0说明还有资源,后面的线程可以继续抢占。否则,寻找安全停靠点,检查中断;如果找不到停靠点或者被唤醒,则继续竞争资源。

 

通知后面的结点

setHeadAndPropagate(Node, int)

 1     private void setHeadAndPropagate(Node node, int propagate) {
 2         Node h = head;
 3         setHead(node); // 设置head指向当前结点
 4         // 如果还有剩余资源,或者是老head结点为空,或者老head结点的等待状态小于0,或者是新head结点为空,或者新head结点的等待状态小于0
 5         if (propagate > 0 || h == null || h.waitStatus < 0 || (h = head) == null || h.waitStatus < 0) {
 6             Node s = node.next; // 后继结点
 7             if (s == null || s.isShared()) // 如果后继结点为空,或者后继结点处于共享模式
 8                 doReleaseShared(); // 释放
 9         }
10     }

 以下几种情况均需要唤醒后面的线程,包括还剩余资源,或者head结点为空,或者head结点的等待状态小于0;或者新的head结点为空,或者新的head结点的等待状态小于0

 

 

释放资源入口

releaseShared(int)

1     public final boolean releaseShared(int arg) {
2         if (tryReleaseShared(arg)) { // 尝试释放资源
3             doReleaseShared(); // 唤醒后面的结点对应的线程
4             return true;
5         }
6         return false;
7     }

共享模式释放资源,也许是多个线程并发释放,所以需要考虑并发问题,同样是CAS操作(CPU指令原语,保证原子性)

 

唤醒后面的线程

doReleaseShared()

 1     private void doReleaseShared() {
 2         for (;;) { // 自旋,由于是共享模式,存在并发问题,CAS操作
 3             Node h = head; // head结点
 4             if (h != null && h != tail) { // 队列不为空
 5                 int ws = h.waitStatus; // 等待状态
 6                 if (ws == Node.SIGNAL) { // 如果是signal,表示后继结点线程需要被唤醒
 7                     if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) // 设置waitStatus为0,防止重复
 8                         continue;
 9                     unparkSuccessor(h); // 唤醒后继结点线程
10                 // 保证传播,设置waitStatus为propagate, 结合setHeadAndPropagate(Node, int), waitStatus < 0时,,调用doReleaseShared()方法
11                 } else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) 
12                     continue;
13             }
14             if (h == head) // 有新的结点入队(空队列时),或者有结点出队,导致head结点改变,需要重新释放
15                 break;
16         }
17     }

共享模式下,多线程并发释放资源,而head唤醒其后继结点后,需要把多出来的资源留给后面的结点;设置新的head结点时,会继续唤醒其后继结点。 

 

传播是什么鬼

共享模式下,要保证释放事件的传播。比如,一个共享资源,可以同时容纳3个线程同时访问。假如,有两个获得资源的线程访问结束,相继释放锁,而此时,临界区外只有一个线程在等待,且已经创建好结点排在同步队列里,就在head结点的后面。

反过来想,假如没有propagate状态,如下图所示。

1. 线程1释放资源,head结点的等待状态,ws = -1,不变,tail结点的线程被唤醒(还没有成为新的head结点,正在竞争资源的路上),此时,已经没有了等待线程;而这个时候,线程2也释放资源,由于head(还是原来的head)结点的等待状态,ws依然是-1(唤醒后继线程),还需要唤醒后面的线程,然而并没有待唤醒的线程;为了不重复这个动作,我们可以在所有的线程释放资源之前,安全地将head结点的ws设置为0,这样就能保证,只有一个线程能设置成功,因此避免了重复唤醒的问题(因为没有多余的等待线程)。

2.接1,但是又有一个问题,假如,在tail线程正在获取资源时,又有新的线程进来了(入队寻找安全点),准备排在了tail结点后面,恰巧tail结点成为新的head结点时,它的ws还是0,老的head结点的ws也是0,调用setHeadAndPropagate方法时,并不会调用doReleaseShared方法(不满足条件),【注意,一个释放事件可能被忽略】,然后新生结点轮到CPU时间片(已经找到了安全点,准备跟前驱结点协商,醒后通知自己),把前驱结点的ws设置为-1,就park了。

3.接2,此时的状态是,资源里面只有两个线程在跑,老的head和老的tail(也是当前head),而新生结点在等待被唤醒,但是资源同时容纳的线程数是3个,当然,又有结点释放资源后,肯定能唤醒新生结点,然而,这降低了并发性。于是乎,propagate状态应运而生。有了它,便能保证不重复唤醒线程,也不落下唤醒事件,而是将唤醒事件传播下去。

4。接3,在doReleaseShared方法里如何保证新的head结点的ws已经被设置为-1了呢,(也许新生结点轮到CPU时间很少);其实不用保证,即使此刻还没协商成功,也没有关系,ws会设置为-3(propagate),唤醒事件已经保留,且会传播下去。等新结点再去设置前驱结点时,发现已经改变,那么便认为不是安全停靠点,于是继续检查,结果发现前驱结点已经是head结点了,太好了,此时可以去竞争资源了,不用被挂起了。

 

取消

cancelAcquire(Node)

 1     private void cancelAcquire(Node node) {
 2         if (node == null) // 为空,忽略
 3             return;
 4         node.thread = null; // 取消,不必记录线程
 5         Node pred = node.prev; // 前驱
 6         while (pred.waitStatus > 0) // 一直往前找,知道遇见第一个有效的结点,并设置为自己的前驱
 7             node.prev = pred = pred.prev;
 8         Node predNext = pred.next; // 前驱的后继结点
 9         node.waitStatus = Node.CANCELLED; // 设置waitStatus为1,取消
10 
11         if (node == tail && compareAndSetTail(node, pred)) { // 如果当前结点是最后一个,直接移除
12             compareAndSetNext(pred, predNext, null);
13         } else {
14             int ws;
15             // 如果前驱不是头结点,而且,前驱的等待状态是SIGNAL或不大于0的情况下设置成SIGNAL,并且线程不为空
16             if (pred != head && ((ws = pred.waitStatus) == Node.SIGNAL
17                     || (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) && pred.thread != null) {
18                 Node next = node.next;
19                 if (next != null && next.waitStatus <= 0) // 当前结点的后继结点不为空,且有效
20                     compareAndSetNext(pred, predNext, next); // 移除当前结点,将其前驱和后继连在一起
21             } else {
22                 unparkSuccessor(node); // 否则,唤醒后继结点(pred是头结点,或者其他的动态情况,比如进来了新的结点,或者有别的结点也取消了,等等)
23             }
24 
25             node.next = node; // 等待被回收
26         }
27     }

线程取消,其所对应的结点,waitStatus设为1(CANCEL),并且尝试移除该结点。首先找出其前面的第一个有效的结点作为其前驱结点,并取得前驱结点的后继(可能是它自己,也可能不是);然后,如果此结点是末尾结点,那么直接移除,并设置前驱为新的末尾(tail)结点,否则查看前驱是否是头结点,以及前驱的等待状态还有线程,如果条件满足,则移除该结点.,将该结点的前驱和后继连接在一起;如果条件不满足,调用唤醒后继结点的方法,由于waitStatus已经设置为CANCEL,最后该结点一定会被移除的(无效),最后等待垃圾回收。

 

条件Condition

等待队列

1         private transient Node firstWaiter; // 等待队列头结点
2         private transient Node lastWaiter; // 等待队列尾结点

 

中断模式

1         private static final int REINTERRUPT = 1; // 在收到信号之后发生过中断
2         private static final int THROW_IE = -1; // 在收到信号之前发生过中断

 

等待

await()

 1         public final void await() throws InterruptedException {
 2             if (Thread.interrupted())
 3                 throw new InterruptedException();
 4             Node node = addConditionWaiter(); // 构造结点,加入到等待队列尾部
 5             int savedState = fullyRelease(node); // 释放锁
 6             int interruptMode = 0;
 7             while (!isOnSyncQueue(node)) { // 判断当前结点是否已经在同步队列里,如果不在,就阻塞当前线程
 8                 LockSupport.park(this);
 9                 if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) // 等待过程中如果发生了中断,则退出循环
10                     break;
11             }
12             // 被唤醒后,重新竞争锁,并记录中断状态
13             if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
14                 interruptMode = REINTERRUPT;
15             if (node.nextWaiter != null)
16                 unlinkCancelledWaiters(); // 清理一波已经取消的结点
17             if (interruptMode != 0)
18                 reportInterruptAfterWait(interruptMode); // 报告中断
19         }

添加结点到等待队列

addConditionWaiter()

 1         private Node addConditionWaiter() {
 2             Node t = lastWaiter; // 末尾结点
 3             if (t != null && t.waitStatus != Node.CONDITION) { // 尾结点取消,清理一波
 4                 unlinkCancelledWaiters();
 5                 t = lastWaiter; // 有效末尾结点
 6             }
 7             Node node = new Node(Thread.currentThread(), Node.CONDITION); // 创建结点
 8             if (t == null)
 9                 firstWaiter = node;
10             else
11                 t.nextWaiter = node;
12             lastWaiter = node; // 加入到尾部
13             return node;
14         }

清除已经取消的结点

unlinkCancelledWaiters()

 1         private void unlinkCancelledWaiters() {
 2             Node t = firstWaiter; // 头结点
 3             Node trail = null; // 蔓延结点,为了记录有效结点,一直向后蔓延;否则,只依赖firstWaiter,末了,还得从尾到头遍历一遍                                                           
 4             while (t != null) {
 5                 Node next = t.nextWaiter; // 后继结点
 6                 if (t.waitStatus != Node.CONDITION) { // 已经取消
 7                     t.nextWaiter = null; // 断开
 8                     if (trail == null) // trail为空,说明还没遇到有效结点,继续往后找
 9                         firstWaiter = next; // 头节点指向下一个
10                     else
11                         trail.nextWaiter = next; // 否则,已经找到有效结点,trail指向的就是目前为止,最靠后的一个有效结点,所以它指向下一个
12                     if (next == null) // 遍历结束
13                         lastWaiter = trail; // 设置尾结点
14                 } else
15                     trail = t; // trail总是指向后一个有效结点
16                 t = next; // 向后找
17             }
18         }

 

 

 释放

fullyRelease(Node)

 1     final int fullyRelease(Node node) {
 2         boolean failed = true;
 3         try {
 4             int savedState = getState(); // 获取状态
 5             if (release(savedState)) { // 释放
 6                 failed = false;
 7                 return savedState;
 8             } else {
 9                 throw new IllegalMonitorStateException();
10             }
11         } finally {
12             if (failed) // 失败,则标记为取消
13                 node.waitStatus = Node.CANCELLED;
14         }
15     }

结点是否在同步队列里

isOnSyncQueue(Node)

 1     final boolean isOnSyncQueue(Node node) {
 2         // 等待状态还是CONDITION,或者前驱为空,则说明不在,因为它不可能是head结点(同步队列里,只有head结点可以为null),而是被head唤醒的
 3         if (node.waitStatus == Node.CONDITION || node.prev == null) 
 4             return false;
 5         if (node.next != null) // 如果有后继结点,说明肯定在同步队列里了
 6             return true;
 7         return findNodeFromTail(node); // 如果后继结点为空,需要从尾部开始查找,因为不断地有新结点加入进来,但也在不远处
 8     }
 9 
10     private boolean findNodeFromTail(Node node) {
11         Node t = tail; // 从尾部开始
12         for (;;) {
13             if (t == node) // 找到返回
14                 return true;
15             if (t == null)
16                 return false;
17             t = t.prev; // 往前移
18         }
19     }

等待过程中是否有中断

checkInterruptWhileWaiting(Node)

1         private int checkInterruptWhileWaiting(Node node) {
2             return Thread.interrupted() ? (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) : 0;
3         }

中断唤醒线程

transferAfterCancelledWait(Node)

1     final boolean transferAfterCancelledWait(Node node) { // interrupt
2         if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) { // 设置成功,直接入队
3             enq(node);
4             return true;
5         }
6         while (!isOnSyncQueue(node)) // 如果设置失败,说明已经丢了一个信号,说不定正在入队,自旋一会儿,等入队完成
7             Thread.yield();
8         return false;
9     }

 

报告中断

reportInterruptAfterWait(int)

1         private void reportInterruptAfterWait(int interruptMode) throws InterruptedException {
2             if (interruptMode == THROW_IE)
3                 throw new InterruptedException();
4             else if (interruptMode == REINTERRUPT)
5                 selfInterrupt();
6         }

 

信号

signal()

1         public final void signal() {
2             if (!isHeldExclusively()) // 非独占式,直接抛异常
3                 throw new IllegalMonitorStateException();
4             Node first = firstWaiter; // 头节点
5             if (first != null)
6                 doSignal(first); // 发布信号
7         }

 

发布信号

doSignal(Node)

1         private void doSignal(Node first) {
2             do {
3                 if ((firstWaiter = first.nextWaiter) == null) // 判断是否为空
4                     lastWaiter = null;
5                 first.nextWaiter = null; // 断开
6             } while (!transferForSignal(first) && (first = firstWaiter) != null); // 结点取消,则往后移动一个,继续通知
7         }

 

唤醒线程

transferForSignal(Node)

 1     final boolean transferForSignal(Node node) { // Signal
 2         if (!compareAndSetWaitStatus(node, Node.CONDITION, 0)) // 修改失败,说明已经取消
 3             return false;
 4         Node p = enq(node); // 入队,返回前驱结点 
 5         int ws = p.waitStatus; // 前驱结点的等待状态
 6         if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL)) // 已经取消,或者waitStatus设置失败,(不是安全停靠点),则唤醒线程
 7             LockSupport.unpark(node.thread);
 8         return true;
 9     }

 

行文至此结束。

 

尊重他人的劳动,转载请注明出处:http://www.cnblogs.com/aniao/p/aniao_aqs.html

 

posted @ 2017-12-25 21:58  林城画序  阅读(549)  评论(1编辑  收藏  举报