技能培训中心网站建设,深圳全网营销网站建设,php语言做的大网站,网站宽度1200px1. Condition的signal方法分析
分为了几个部分#xff1a; ● 确保执行signal方法的是持有锁的线程 ● 脱离Condition的队列 ● 将Node状态从-2改为0 ● 将Node添加到AQS队列 ● 为了避免当前Node无法在AQS队列正常唤醒做了一些判断和操作
// 线程挂起后#xff0c;可以基于…1. Condition的signal方法分析
分为了几个部分 ● 确保执行signal方法的是持有锁的线程 ● 脱离Condition的队列 ● 将Node状态从-2改为0 ● 将Node添加到AQS队列 ● 为了避免当前Node无法在AQS队列正常唤醒做了一些判断和操作
// 线程挂起后可以基于signal唤醒~
public final void signal() {// 在ReentrantLock中如果执行signal的线程没有持有锁资源直接扔异常
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
// 拿到排在Condition首位的Node
Node first firstWaiter;
// 有Node在排队才需要唤醒如果没有直接告辞~~
if (first ! null)
doSignal(first);
}
// 开始唤醒Condition中的Node中的线程
private void doSignal(Node first) {
// 先一波do-while走你~~~
do {
// 获取到第二个节点并且将第二个节点设置为firstWaiter
if ( (firstWaiter first.nextWaiter) null)
// 说明就一个节点在Condition队列中那么直接将firstWaiter和lastWaiter置位null
lastWaiter null;
// 如果还有nextWaiter节点因为当前节点要被唤醒了脱离整个Condition队列。将nextWaiter置位null
first.nextWaiter null;
// 如果transferForSignal返回true一切正常退出while循环
} while (!transferForSignal(first)
// 如果后续节点还有往后面继续唤醒如果没有退出while循环
(first firstWaiter) ! null);
}
// 准备开始唤醒在Condition中排队的Node
final boolean transferForSignal(Node node) {
// 将在Condition队列中的Node的状态从-2改为0代表要扔到AQS队列了。
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
// 如果失败了说明在signal之前应当是线程被中断了从而被唤醒了。
return false;
// 如果正常的将Node的状态从-2改为0这是就要将Condition中的这个Node扔到AQS的队列。
// 将当前Node扔到AQS队列返回的p是当前Node的prev
Node p enq(node);
// 获取上一个Node的状态
int ws p.waitStatus;
// 如果ws 0 说明这个Node已经被取消了。
// 如果ws状态不是取消将prev节点的状态改为-1,。
if (ws 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
// 如果prev节点已经取消了可能会导致当前节点永远无法被唤醒。立即唤醒当前节点基于acquireQueued方法
// 让当前节点找到一个正常的prev节点并挂起线程
// 如果prev节点正常但是CAS修改prev节点失败了。证明prev节点因为并发原因导致状态改变。还是为了避免当前
// 节点无法被正常唤醒提前唤醒当前线程基于acquireQueued方法让当前节点找到一个正常的prev节点并挂起线程
LockSupport.unpark(node.thread);
// 返回true
return true;
}
2. Conditiond的await方法分析后置分析
分为了几个部分 ● 唤醒之后要先确认是中断唤醒还是signal唤醒还是signal唤醒后被中断 ● 确保当前线程的Node已经在AQS队列中 ● 执行acquireQueued方法等待锁资源。 ● 在获取锁资源后要确认是否在获取锁资源的阶段被中断过如果被中断过并且不是THROW_IE那就确保 interruptMode是REINTERRUPT。 ● 确认当前Node已经不在Condition队列中了 ● 最终根据interruptMode来决定具体做的事情 ● 0嘛也不做。 ● THROW_IE抛出异常 ● REINTERRUPT执行线程的interrupt方法
// 现在分析await方法的后半部分
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node addConditionWaiter();
int savedState fullyRelease(node);
// 中断模式~
int interruptMode 0;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
// 如果线程执行到这说明现在被唤醒了。
// 线程可以被signal唤醒。如果是signal唤醒可以确认线程已经在AQS队列中
// 线程可以被interrupt唤醒线程被唤醒后没有在AQS队列中。
// 如果线程先被signal唤醒然后线程中断了。。。。做一些额外处理
// checkInterruptWhileWaiting可以确认当前中如何唤醒的。
// 返回的值有三种
// 0正常signal唤醒没别的事不知道Node是否在AQS队列// THROW_IE-1中断唤醒并且可以确保在AQS队列
// REINTERRUPT1signal唤醒但是线程被中断了并且可以确保在AQS队列
if ((interruptMode checkInterruptWhileWaiting(node)) ! 0)
break;
}
// Node一定在AQS队列
// 执行acquireQueued尝试在ReentrantLock中获取锁资源。
// acquireQueued方法返回true代表线程在AQS队列中挂起时被中断过
if (acquireQueued(node, savedState) interruptMode ! THROW_IE)
// 如果线程在AQS队列排队时被中断了并且不是THROW_IE状态确保线程的interruptMode是REINTERRUPT
// REINTERRUPTawait不是中断唤醒但是后续被中断过
interruptMode REINTERRUPT;
// 如果当前Node还在condition的单向链表中脱离Condition的单向链表
if (node.nextWaiter ! null)
unlinkCancelledWaiters();
// 如果interruptMode是0说明线程在signal后以及持有锁的过程中没被中断过什么事都不做
if (interruptMode ! 0)
// 如果不是0~
reportInterruptAfterWait(interruptMode);
}
// 判断当前线程被唤醒的模式确认interruptMode的值。
private int checkInterruptWhileWaiting(Node node) {
// 判断线程是否中断了。
return Thread.interrupted() ?
// THROW_IE代表线程是被interrupt唤醒的需要向上排除异常
// REINTERRUPT代表线程是signal唤醒的但是在唤醒之后被中断了。(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
// 线程是正常的被signal唤醒并且线程没有中断过。
0;
}
// 判断线程到底是中断唤醒的还是signal唤醒的
final boolean transferAfterCancelledWait(Node node) {
// 基于CAS将Node的状态从-2改为0
if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
// 说明是中断唤醒的线程。因为CAS成功了。
// 将Node添加到AQS队列中~如果是中断唤醒的当前线程同时存在Condition的单向链表以及AQS的队列中
enq(node);
// 返回true
return true;
}
// 判断当前的Node是否在AQS队列signal唤醒的但是可能线程还没放到AQS队列
// 等到signal方法将线程的Node扔到AQS队列后再做后续操作
while (!isOnSyncQueue(node))
// 如果没在AQS队列上那就线程让步稍等一会Node放到AQS队列再处理看CPU
Thread.yield();
// signal唤醒的返回false
return false;
}
// 确认Node是否在AQS队列上
final boolean isOnSyncQueue(Node node) {
// 如果线程状态为-2肯定没在AQS队列// 如果prev节点的值为null肯定没在AQS队列
if (node.waitStatus Node.CONDITION || node.prev null)
// 返回false
return false;
// 如果节点的next不为null。说明已经在AQS队列上。、
if (node.next ! null)
// 确定AQS队列上有
return true;
// 如果上述判断都没有确认节点在AQS队列上在AQS队列中寻找一波
return findNodeFromTail(node);
}
// 在AQS队列中找当前节点
private boolean findNodeFromTail(Node node) {
// 拿到尾节点
Node t tail;
for (;;) {
// tail是否是当前节点如果是说明在AQS队列
if (t node)
// 可以跳出while循环
return true;
// 如果节点为nullAQS队列中没有当前节点
if (t null)
// 进入while让步一手
return false;
// t向前引用
t t.prev;
}
}
private void reportInterruptAfterWait(int interruptMode) throws InterruptedException {
// 如果是中断唤醒的await直接抛出异常
if (interruptMode THROW_IE)
throw new InterruptedException();
// 如果是REINTERRUPTsignal后被中断过
else if (interruptMode REINTERRUPT)
// 确认线程的中断标记位是true
// Thread.currentThread().interrupt();
selfInterrupt();
}
3 Condition的awaitNanossignalAll方法分析
awaitNanos仅仅是在await方法的基础上做了一内内的改变整体的逻辑思想都是一样的。 挂起线程时传入要阻塞的时间时间到了自动唤醒走添加到AQS队列的逻辑
// await指定时间多了个时间到了自动醒。
public final long awaitNanos(long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node addConditionWaiter();
int savedState fullyRelease(node);
// deadline当前线程最多挂起到什么时间点final long deadline System.nanoTime() nanosTimeout;
int interruptMode 0;
while (!isOnSyncQueue(node)) {
// nanosTimeout的时间小于等于0直接告辞
if (nanosTimeout 0L) {
// 正常扔到AQS队列
transferAfterCancelledWait(node);
break;
}
// nanosTimeout的时间大于1000纳秒时才可以挂起线程
if (nanosTimeout spinForTimeoutThreshold)
// 如果大于正常挂起
LockSupport.parkNanos(this, nanosTimeout);
if ((interruptMode checkInterruptWhileWaiting(node)) ! 0)
break;
// 计算剩余的挂起时间可能需要重新的走while循环再次挂起线程
nanosTimeout deadline - System.nanoTime();
}
if (acquireQueued(node, savedState) interruptMode ! THROW_IE)
interruptMode REINTERRUPT;
if (node.nextWaiter ! null)
unlinkCancelledWaiters();
if (interruptMode ! 0)
reportInterruptAfterWait(interruptMode);
// 剩余的挂起时间
return deadline - System.nanoTime();
}
signalAll方法。这个方法一看就懂之前signal是唤醒1个这个是全部唤醒
// 以do-while的形式将Condition单向链表中的所有Node全部唤醒并扔到AQS队列
private void doSignalAll(Node first) {
// 将头尾都置位null~
lastWaiter firstWaiter null;
do {
// 拿到next节点的引用
Node next first.nextWaiter;
// 断开当前Node的nextWaiter
first.nextWaiter null;
// 修改Node状态扔AQS队列是否唤醒
transferForSignal(first);
// 指向下一个节点
first next;
} while (first ! null);
}