参考原文链接:https://blog.csdn.net/anlian523/article/details/106653034
目录
Condition源码分析
Condition队列和Sync队列比较
- condition队列是个单向链表,但是Sync是个双向链表。
- condition入队是有锁的(准备释放),但是Sync入队是无锁的
- condition出队是无锁的,Sync出队是因为获取到锁
- 因为conditionObject是AQS的内部类,所以迁移起来很方便,可以通过AQS的方法直接来进行把队列进行位置迁移。Sync实现了AQS可以做到这样的事
public abstract class AbstractQueuedSynchronizer{
private transient volatile Node head;
private transient volatile Node tail;
public class ConditionObject implements Condition {
private transient Node firstWaiter;
private transient Node lastWaiter;
}
}
newCondition()
- 就是ReentrantLock的一个获取Condition的方法,通过Sync,不能够被继承
public Condition newCondition() {
return sync.newCondition();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
await
- 把当前线程存入node加入到队列
- 释放线程所有的锁
- 如果线程离开了同步队列,那么线程就要进入阻塞。
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);
//被打断或者是唤醒,结束循环
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
addConditionWaiter
- 首先判断队尾是不是condition状态,而且是不是空的,如果不是condition状态,那么就要清理队列并且重新设定好队尾
- 如果是condition状态那么就创建节点(线程),并且放到队尾,如果队尾是空的,那么就让firstWaiter指向新节点,否则就是加入到队尾,并且设置新节点是队尾
- 单链表只需要维持后继即可
- 中断和signal都会把condition从-2到0
private Node addConditionWaiter() {
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
//判断队尾是不是condition,不是就清理,设置队尾
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
//创建节点,并且存入线程,加入队尾或者是队尾是空那么就firstWaiter指向第一个节点
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
unlinkCancelledWaiters
- 其实就是简单的单链表删除操作,遍历循环链表trail相当于就是最近的condition节点,t就是在往下面去遍历找到不是condition的节点。但如果是condition节点,相当于就是t往前面移动一个,trail往前面移动一次。
- trail相当于就是前驱节点,也就是队列里面符合condition条件的最后一个节点,t找到删除节点,那么是不是就trail.next=t.next就把它给删了,如果没有trail就无法删除节点。
- 就是单链表删除操作。找到condition就删了。
private void unlinkCancelledWaiters() {
Node t = firstWaiter;
Node trail = null;
while (t != null) {
//记录t下一个节点
Node next = t.nextWaiter;
//如果发现不是condition的节点,目标删除节点
if (t.waitStatus != Node.CONDITION) {
//先断开连接t和下一个节点的连接
t.nextWaiter = null;
//如果前驱节点是null,说明才刚开始遍历,那么队列只有两个节点,直接把firstWaiter赋值next,就相当于跳过了t这个节点,也就删除了
if (trail == null)
firstWaiter = next;
else
//如果不是null,那么前驱节点还是跳过t,nextWaiter指向下一个节点
trail.nextWaiter = next;
//如果next是空的那么trail(因为判断到这肯定是非空),那么最后lastWaiter指向trail就可以了。
if (next == null)
lastWaiter = trail;
}
else//
trail = t;
t = next;
}
}
fullyRelease
- 其实就是通过release来释放所有的锁。先获取当前线程占据多少锁的状态。然后release
- 如果有人没有锁的时候调用await,那么就会抛出异常而且把节点设置为cancelled(对应上面的删除节点)
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;
}
}
isOnSyncQueue
- 第一个判断就是如果不是condition而且有前驱的话那么就是可能在sync队列里
- 如果next不是null那么只要在sync队里面可能有next
- 如果node.next是空,那么还需要从尾到前确定node是不是就是在sync队伍里面,还是说还存在condition队里
final boolean isOnSyncQueue(Node node) {
//如果不是condition和有前驱节点,那么可能入到sync里面了
if (node.waitStatus == Node.CONDITION || node.prev == null)
return false;
//如果next不为空,那么只有入sync的情况才有可能
if (node.next != null) // If has successor, it must be on queue
return true;
//从尾到前遍历找到node,可能是队尾,也可能是enq的分叉问题
return findNodeFromTail(node);
}
private boolean findNodeFromTail(Node node) {
//从尾到前遍历找到node,可能是队尾,也可能是enq的分叉问题
Node t = tail;
for (;;) {
if (t == node)
return true;
if (t == null)
return false;
t = t.prev;
}
}
signalAll释放Condition的队列
- 首先就是获取单链表头
- 然后调用doSignalAll来唤醒整个队列的线程。
public final void signalAll() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignalAll(first);
}
doSignalAll
- 这里其实就是把单链表的头结点和尾节点设置为空,然后使用传入过来的头结点,相当于就是不让其他线程访问到这个单链表。
- 下面就是进行转移,把节点转移到Sync队列
- 把节点放入Sync队尾,并且获取前驱节点。
- 下面!compareAndSetWaitStatus(p, ws, Node.SIGNAL)保证前驱节点的状态是signal如果是false,那么就要去唤醒线程,执行shouldParkAfterFailedAcquiree,间接保证前驱节点是signal。最后返回成功
private void doSignalAll(Node first) {
lastWaiter = firstWaiter = null;
do {
//转移链表所有的节点
Node next = first.nextWaiter;
first.nextWaiter = null;
transferForSignal(first);
first = next;
} while (first != null);
}
transferForSignal
方便理解下面的场景,什么时候signal来了会唤醒,什么时候不会唤醒
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))条件解释?
-
transferForSignal实际上做的就是转移node,前提node是condition队列的
-
ws<=0而且设置compareAndSetWaitStatus(p, ws, Node.SIGNAL))失败说明前驱节点并不是signal,那么就通过LockSupport.unpark(node.thread);唤醒队列进入到acquireQueue来间接设置前驱节点状态保证是signal。
-
如果一开始就设置signal成功就不会唤醒队列等待其它线程unlock来唤醒当前node
final boolean transferForSignal(Node node) {
//把节点状态改成0,如果中断也是把节点状态改为0
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
//把节点放入队尾并且获取前驱节点
Node p = enq(node);
//如果前驱节点是大于0说明哈没有被提示要唤醒后面的节点。
int ws = p.waitStatus;
//尝试去修改前驱节点的状态
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
signal
- 实际上就是执行doSignal,传入first参数。
- 然后doSignal处理方法就是让firstWaiter往后走一个,然后first脱链,接着就是尝试转移first。被转移的first必须是condition,其它跟上面一样。因为都是调用transferForSignal
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
假设前面的signal都没有被中断,那么什么时候会执行完await?
- 当signal没有唤醒node线程的时候。
- 那么node在数据结构上在Sync上面
- node的线程仍然阻塞在await的park上
- 当另一个执行unlock的线程释放这个独享锁,那么才会去unpark来唤醒node线程
- 当signal唤醒了node线程
- 数据结构在Sync上
- 线程已经尝试恢复执行但是阻塞在了Sync的shouldParkAfterFailedAcquire
结果还是需要unlock的线程来释放锁才能够await完全执行完成
signal前中断线程的过程?
- THROW_IE会抛出异常、REINTERRUPT不会抛出异常,这两种都是被中断的状态而且会直接结束循环。
- 中断线程调用checkInterruptWhileWaiting查看是不是被中断了,如果是那么就尝试调用transferAfterCancelledWait(node)来锁定状态修改并且把节点加入到队列。
为什么这里CAS可能会失败?
- 因为signal和中断在竞争,谁到谁把线程放到Sync队列防止重复。
- 这里的while (!isOnSyncQueue(node))
Thread.yield();停顿的原因就是为了让节点enq完全送入Sync的时候才执行acquireQueue(其实就是await线程唤醒后重新进入到Sync阻塞的方法)
当前情景下只是进行了中断而且没有进行signal
- 那么就是中断之后结束循环重新进入到Sync队列上
- node仍然在Condition,因为first.nextWaiter没有打断,但是也在Sync队列上
- 线程开始进入到Sync的队列进行阻塞。
中断和signal的区分
- 中断其实是中断线程A中断当前线程B,也就是node线程。
- signal其实就是signal线程想要唤醒线程B,但是signal线程会遍历condition链表找到线程B节点,并且会在转移节点transferForSignal中执行compareAndSetWaitStatus(node, Node.CONDITION, 0),而中断之后就是node线程B在运行执行到checkInterruptWhileWaiting的transferAfterCancelledWait也会进行compareAndSetWaitStatus(node, Node.CONDITION, 0)来进行竞争,主要就是想把节点搬到Sync然后执行acqurieQueue,让线程B去到Sync队列中阻塞。
private int checkInterruptWhileWaiting(Node node) {
return Thread.interrupted() ?
(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
0;
}
final boolean transferAfterCancelledWait(Node node) {
if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
enq(node);
return true;
}
//为了让signal的enq把节点送到Sync才进行acquireQueue继续操作
while (!isOnSyncQueue(node))
Thread.yield();
return false;
}
关于中断没有那node移除出condition队列怎么办?
- 其实是acquireQueued(node, savedState)之后(线程获取之前释放的所有锁),再次判断interruptMode是什么类型
- 如果是0那么就是没有中断升级中断类型
- 如果是REINTERRUPT那么就设置回REINTERRUPT没啥变化
- 如果是THROW_IE那么没必要去降级了
- 接着假设发生中断那么就会执行unlinkCancelledWaiters();删除之前设置不是condition的节点
- 最后执行的 reportInterruptAfterWait(interruptMode);就是根据中断类型进行处理
- 第一次中断比acquireQueue的中断更为重要
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);
//判断是否中断
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
//重新进入Sync队列
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
private void reportInterruptAfterWait(int interruptMode)
throws InterruptedException {
//抛出异常
if (interruptMode == THROW_IE)
throw new InterruptedException();
//重新对中断进行标记。
else if (interruptMode == REINTERRUPT)
selfInterrupt();
}
await总结
await的持有锁过程
- await进来的时候有锁
- 执行fullyRelease之后无锁
- acquireQueue之后又有锁
- signal和signalAll必须要有锁才能唤醒await的线程
整个过程总结
- await进来先释放所有锁
- 然后封装成节点进入condition队列
- 进入阻塞
- 中断/signal竞争,线程节点转移回Sync队列,并且唤醒线程执行acquire,如果又中断那么就设置好中断级别
- 最后就是如果有中断就删除之前中断的节点,并且执行对应中断级别的处理。await结束
awaitUninterruptibly
- 其实和await不同的地方就是不需要对中断做太多处理,而且不允许中断立刻进入acquireQueue
- 函数开头也不需要删除中断
- 只需要记录中断或者没中断的状态
- 在await中断或者是在acquireQueued(node, savedState)的时候中断都自我中断一次
- 只有signal才能唤醒线程
public final void awaitUninterruptibly() {
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
boolean interrupted = false;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if (Thread.interrupted())
interrupted = true;
}
//在await中断或者是在acquireQueued(node, savedState)的时候中断都自我中断一次
if (acquireQueued(node, savedState) || interrupted)
//再次设置中断,告诉线程被中断过
selfInterrupt();
}
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
awaitNacos
- 其实就是做了一个定时任务,如果不能在限定时间阻塞获取锁,都会返回剩余等待时间。可以在超时之后直接执行acquireQueue
- 如果有signal来过,而且超时,那么还是执行acquireQueue,如果signal没有来过那么就还在while循环里面等待nanosTimeout剩余变成负数,那么就会结束循环进入acquireQueue
- 当超时的时候,signal来过才会转移node到Sync队列那么!isOnSyncQueue(node)失败,但是如果signal没有来过说明节点还没有进行转移那么!isOnSyncQueue(node)成功,而且超时那么会执行 transferAfterCancelledWait(node);来转移节点到Sync队列
出队的条件
- node进入Sync,可能是signal线程做的,也可能是超时的时候做的。
- 还有一种就是因为超时,转移节点之后推出循环
public final long awaitNanos(long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
//最终超时时间
final long deadline = System.nanoTime() + nanosTimeout;
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
//如果已经超时那么就转移节点到Sync队列不再进行等待
if (nanosTimeout <= 0L) {
transferAfterCancelledWait(node);
break;
}
//如果剩余等待时间很少,那么就不进行阻塞,让它在while自旋
if (nanosTimeout >= spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
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();
}
await(long time, TimeUnit unit)
- 多设置了时间单位,而且boolean类型的timeout来记录是不是超时问题。
await和Nacos不同的地方?
一个场景,signal来过导致while循环结束,但是由于unlock线程执行太久,导致await很久才执行。
- 对于await来说,返回的只是原因,它并没有超时,而是signal唤醒(反应原因)
- 对于awaitNacos来说最后的return deadline - System.nanoTime();处理出现的问题就是返回的是负数,因为unlock等待时间很长,那么导致他们两个的答案是不一样的。(反应超时时间,包括在acquireQueue的超时)
public final boolean await(long time, TimeUnit unit)
throws InterruptedException {
long nanosTimeout = unit.toNanos(time);
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
final long deadline = System.nanoTime() + nanosTimeout;
boolean timedout = false;
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
if (nanosTimeout <= 0L) {
timedout = transferAfterCancelledWait(node);
break;
}
if (nanosTimeout >= spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
nanosTimeout = deadline - System.nanoTime();
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null)
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
return !timedout;
}
awaitUnit
public final boolean awaitUntil(Date deadline)
throws InterruptedException {
long abstime = deadline.getTime(); //不同处1,直接获取过期时间
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
// 不同处2
boolean timedout = false;
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
if (System.currentTimeMillis() > abstime) {
//不同处3,大于过期时间就是超时
timedout = transferAfterCancelledWait(node);
break;
}
LockSupport.parkUntil(this, abstime);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null)
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
return !timedout;
}