个人对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()就释放对应线程的锁。