个人对ReentrantLock的Condition的一点理解(JDK8源码)

Condition的一些参数介绍:

1 public class ConditionObject implements Condition, java.io.Serializable {
2         /** 条件队列的第一个节点. */
3         private transient Node firstWaiter;
4        /** 条件队列的最后一个节点. */
5         private transient Node lastWaiter;

Condition.awai()源码开始解析:

1         /** 标志线程需要再次执行中断操作 */
2         private static final int REINTERRUPT =  1;
3         /** 标志线程直接抛出中断异常 */
4         private static final int THROW_IE    = -1;
 1 public final void await() throws InterruptedException {
 2             // 若线程被中断过,直接抛出中断异常
 3             if (Thread.interrupted())
 4                 throw new InterruptedException();
 5             // 为当前线程创建条件节点并添加到条件队列的尾部
 6             Node node = addConditionWaiter();
 7             // 全部释放当前节点占用的锁,并返回释放锁的数量
 8             int savedState = fullyRelease(node);
 9             int interruptMode = 0;
10             // 在同步队列中()查询node节点是否存在,不存在就阻塞该线程
11             while (!isOnSyncQueue(node)) {
12                 LockSupport.park(this);
13                 // 被唤醒后,判断线程中途是否被中断过
14                 // 若中断过,直接跳出循环
15                 // 若没,则重新判断条件,因被其他线程唤醒,则当前节点在同步队列中已存在,跳出循环
16                 if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
17                     break;
18             }
19             // 重新尝试获取之前获取的锁的数量并检查是否线程中断过
20             if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
21                 interruptMode = REINTERRUPT;
22             // 若获取锁失败或线程已处理中断了,则清理该中断线程
23             if (node.nextWaiter != null) // clean up if cancelled
24                 unlinkCancelledWaiters();
25             // 根据线程中断的情况处理相应逻辑
26             if (interruptMode != 0)
27                 reportInterruptAfterWait(interruptMode);
28         }

接下来按顺序介绍期间调用的方法,添加条件节点addConditionWaiter:

 1 private Node addConditionWaiter() {
 2             Node t = lastWaiter;
 3             // 判断最后一个节点是否是条件节点
 4             if (t != null && t.waitStatus != Node.CONDITION) {
 5                 // 若不是,则清理之
 6                 unlinkCancelledWaiters();
 7                 // 重新标记t为清理后的条件队列的最后一个节点
 8                 t = lastWaiter;
 9             }
10             // 为当前线程生成一个条件节点,并扔在尾部
11             Node node = new Node(Thread.currentThread(), Node.CONDITION);
12             if (t == null)
13                 firstWaiter = node;
14             else
15                 t.nextWaiter = node;
16             lastWaiter = node;
17             return node;
18         }
unlinkCancelledWaiters()清理非条件节点的方法:
 1 private void unlinkCancelledWaiters() {
 2             Node t = firstWaiter;
 3             // 用于记录遍历到的最新的一个条件节点
 4             Node trail = null;
 5             // 从第一个等待节点开始遍历
 6             while (t != null) {
 7                 Node next = t.nextWaiter;
 8                 // 判断当前等待节点是不是条件节点
 9                 if (t.waitStatus != Node.CONDITION) {
10                     // 清理当前节点
11                     t.nextWaiter = null;
12                     // 若还没有条件节点,将下一个等待节点标记为第一个节点
13                     if (trail == null)
14                         firstWaiter = next;
15                     else
16                         // 记录条件节点的下一个节点
17                         trail.nextWaiter = next;
18                     // 表示已遍历到尾部节点,那最近的一个条件trail就是尾部节点
19                     // 并且当next == null成立,则下面t = next的t为空了,循环结束了
20                     if (next == null)
21                         lastWaiter = trail;
22                 }
23                 else
24                     // 记录trail为条件节点
25                     trail = t;、
26                 // 继续下一次循环
27                 t = next;
28             }
29         }
fullyRelease(Node node)全部释放锁:
 1 final int fullyRelease(Node node) {
 2         boolean failed = true;
 3         try {
 4             // 获取当前线程占用锁的数量
 5             int savedState = getState();
 6             // 释放对应锁的数量
 7             if (release(savedState)) {
 8                 failed = false;
 9                 return savedState;
10             } else {
11                 throw new IllegalMonitorStateException();
12             }
13         } finally {
14             if (failed)
15                 node.waitStatus = Node.CANCELLED;
16         }
17     }
isOnSyncQueue(Node node)判断是否在同步队列:
 1 final boolean isOnSyncQueue(Node node) {
 2         // node是条件节点或node节点不在同步队列
 3         if (node.waitStatus == Node.CONDITION || node.prev == null)
 4             return false;
 5         // node有后继节点,表示node肯定在同步队列
 6         if (node.next != null)
 7             return true;
 8         // 在同步队列中从尾部节点开始遍历查找node
 9         return findNodeFromTail(node);
10     }
findNodeFromTail方法:
 1 private boolean findNodeFromTail(Node node) {
 2         Node t = tail;
 3         for (;;) {
 4             // 表示node节点在同步队列中
 5             if (t == node)
 6                 return true;
 7             // 已遍历到head节点,表示node不在同步队列中
 8             if (t == null)
 9                 return false;
10             t = t.prev;
11         }
12     }
checkInterruptWhileWaiting方法:
1 // 检查线程是否是中断状态
2 private int checkInterruptWhileWaiting(Node node) {
3             return Thread.interrupted() ?
4                 (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
5                 0;
6         }
1  /** 标志线程需要再次执行中断操作 */
2 private static final int REINTERRUPT =  1;
3  /** 标志线程直接抛出中断异常 */
4 private static final int THROW_IE    = -1;

transferAfterCancelledWait方法:
 1 // 此时线程已是中断状态
 2 final boolean transferAfterCancelledWait(Node node) {
 3         // 因线程是通过signal解除阻塞的,则此时node节点状态应是signal
 4         // 若CAS更新成功,表示线程是因中断操作才解除阻塞的
 5         // 此时应将node节点放入同步队列中
 6         if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
 7             enq(node);
 8             return true;
 9         }
10         // 线程通过signal解除阻塞时,node节点应在同步队列中
11         // 若while为true,表示还在条件队列中,应重新去抢占锁
12         while (!isOnSyncQueue(node))
13             Thread.yield();
14         return false;
15     }
reportInterruptAfterWait(interruptMode)根据中断模式来执行对应操作:
1 private void reportInterruptAfterWait(int interruptMode)
2             throws InterruptedException {
3             if (interruptMode == THROW_IE)
4                 throw new InterruptedException();
5             else if (interruptMode == REINTERRUPT)
6                 selfInterrupt();
7         }

接下来分析下Condition.singal()方法

1 public final void signal() {
2             // 只有拥有独占锁才能执行signal操作
3             if (!isHeldExclusively())
4                 throw new IllegalMonitorStateException();
5             // 解除第一个等待节点的阻塞状态
6             Node first = firstWaiter;
7             if (first != null)
8                 doSignal(first);
9         }
doSignal(node)方法:
 1 private void doSignal(Node first) {
 2             do {
 3                 // 表示只有一个节点,则将尾部节点设置为空
 4                 if ( (firstWaiter = first.nextWaiter) == null)
 5                     lastWaiter = null;
 6                 // 清理第一个节点
 7                 first.nextWaiter = null;
 8             } while (!transferForSignal(first) &&
 9                      (first = firstWaiter) != null);
10         }
transferForSignal(node)方法:
 1 final boolean transferForSignal(Node node) {
 2         // 若CAS更新失败,表示当前节点已被取消
 3         if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
 4             return false;
 5         // 将node节点放入到同步队列
 6         Node p = enq(node);
 7         int ws = p.waitStatus;
 8         // node节点被取消才会执行解除阻塞操作
 9         if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
10             LockSupport.unpark(node.thread);
11         return true;
12     }

这里有个疑问,为什么node节点被取消才会执行解除阻塞操作??求大神解答。

 

总结:

1. Condition的实现其实就是把在同步队列的节点放入到条件队列中,并阻塞;

2. 解除阻塞只会按FIFO顺序解除,跟公平锁与非公平锁无关;

3. 当执行signal()方法后,在unlock()方法之后才会唤醒其他线程,而不是一执行signal()就释放对应线程的锁。

 

 
 

posted on 2018-01-29 14:00  西红柿&番茄  阅读(283)  评论(0编辑  收藏  举报

导航