await源码
public class ConditionObject implements Condition, java.io.Serializable {
private static final long serialVersionUID = 1173984872572414699L;
/** First node of condition queue. */
private transient Node firstWaiter;
/** Last node of condition queue. */
private transient Node lastWaiter;
/**
* Creates a new {@code ConditionObject} instance.
*/
public ConditionObject() { }
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);
}
private Node addConditionWaiter() {
Node t = lastWaiter;
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;
else
//只有1个指针nextWaiter
//说明是1个单向队列
t.nextWaiter = node;
lastWaiter = node;
return node;
}
}
public class await {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(() -> {
lock.lock();
try {
//10个线程依次获取锁,然后执行await方法
//此时这10个线程对应10个node放在队列里
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
});
thread.start();
}
}
}
线程等待队列是双向链表即同步队列是双向链表,Codition等待队列是单链表
await
package test;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionObject implements Condition, java.io.Serializable {
private static final long serialVersionUID = 1173984872572414699L;
// 第一个等待节点 初始都是null
private transient Node firstWaiter;
// 最后一个等待节点 初始都是null
private transient Node lastWaiter;
public ConditionObject() { }
//await方法
public final void await() throws InterruptedException {
//如果调用await的线程已经是中断状态抛出异常
if (Thread.interrupted()){
throw new InterruptedException();
}
//添加节点入Condition等待队列
//进入addConditionWaiter
Node node = addConditionWaiter();
// 释放当前线程所有的重入锁 并唤醒后继节点
// 因为当前线程的锁释放后肯定要唤醒其他等待的节点去获取锁
// 这说明了调用await方法必须是已经获得了condition引用(关联)的lock。
int savedState = fullyRelease(node);
//默认的打断模式是 0 即没有被打断
int interruptMode = 0;
/***
isOnSyncQueue(node)判断是否在同步队列
此时是不在同步队列:所以返回false
!isOnSyncQueue(node)=true
然后进入 LockSupport.park(this);阻塞
**/
while (!isOnSyncQueue(node)) {
//将当前线程park
LockSupport.park(this);
//思考下:什么情况下会从park的状态解除?
//1.当前线程被interrupt
//2.当前线程被signal即LockSupport.unpark
//当从park的状态解除以后,会调用checkInterruptWhileWaiting()方法
//判断自己在park过程中有没有被中断过。
//返回0表示没有被中断过
//返回THROW_IE = -1 表示需要抛出异常,因为只有在signal之前才能cas将0变成-2,进入同步队列成功。
//返回1表示需要重置中断标识
/***
思考:什么时候会在同步队列?即什么时候跳出循环?
1.当前节点被移动到同步队列,即其他线程调用condition的signal或者signalAll,并把当前节点加入同步队列并修改状态为0
2.在signal方法之前或者之后被interrupt,interruptMode!=0主动执行break;跳出循环
如果是signal方法之前被interrupt 返回-1表示需要抛出异常
如果是signal方法之后被interrupt 返回1表示需要重置中断标识
因为只有在signal之前的interrupt才能cas将0变成-2,进入同步队列成功。
因为只有在signal之前的interrupt才能cas将0变成-2,进入同步队列成功。
因为只有在signal之前的interrupt才能cas将0变成-2,进入同步队列成功。
为什么?
因为如果先执行signal,必然是由执行signal方法的线程把当前节点加入同步队列并修改状态为0
否则cas是成功不了的
当退出while循环后就会调用acquireQueued(node, savedState)
acquireQueued的作用是在自旋过程中不断尝试获取同步状态,直至成功获取到lock。
***/
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0){
break;
}
}
// 打断模式 - 在退出等待时重新设置打断状态
// private static final int REINTERRUPT = 1;
// 打断模式 - 在退出等待时抛出异常
//private static final int THROW_IE = -1;
//前提:(线程没有被打断并且被调用了signal方法被唤醒) 或者 (是在signal之后被打断)
//那么就尝试去获取锁
//如果获取不到就继续在同步队列park阻塞直到获取到锁
//如果获取到锁,并且打断是在signal之后
//interruptMode = REINTERRUPT;
//这里无视打断模式也是不可打断的体现
if (acquireQueued(node, savedState) && interruptMode != THROW_IE){
interruptMode = REINTERRUPT;
}
//当在同步队列中获取到锁继续往下走 如果当前节点的下一个节点 不为空 帮忙清理在等待队列被取消的节点
// 清除等待队列中被取消的线程
if (node.nextWaiter != null){
unlinkCancelledWaiters();
}
if (interruptMode != 0){
//说明被打断
reportInterruptAfterWait(interruptMode);
}
}
addConditionWaiter:进入等待队列
// ㈠ 添加一个 Node 至等待队列
private Node addConditionWaiter() {
//获取最后一个节点
Node t = lastWaiter;
//Node.CONDITION=-2 表示结点等待在Condition上
//不等于-2 代表 尾结点没有在condition等待队列上
//
if (t != null && t.waitStatus != Node.CONDITION) {
//将所有已取消的 Node 从队列链表断开
unlinkCancelledWaiters();
t = lastWaiter;
}
// 这里很重要 这里很重要 这里很重要
// 创建一个关联当前线程的新Node,并设置状态为-2
// 创建一个关联当前线程的新Node,并设置状态为-2
// 创建一个关联当前线程的新Node,并设置状态为-2
// 后面在判断节点在await的时候是否被interrupt,interrupt是在signal之前打断还是之后打断使用的是cas
// 原始值就是Node.CONDITION=-2
Node node = new Node(Thread.currentThread(), Node.CONDITION);
//如果尾节点是空
if (t == null)
//将新加入的节点给头结点
firstWaiter = node;
else
//将新加入的节点给尾节点的下一个节点
//添加第一个node的时候n1即是头也是尾
//添加第二个node的时候n1是头n2是尾 相当于将第二个节点n2放入头结点n1的下一个节点
//添加第三个node的时候n3是尾相当于将第3个节点放入原尾结点n2的下一个节点
//所以这里是个单向链表
t.nextWaiter = node;
//最后把lastWaiter指向新加入的node
lastWaiter = node;
return node;
}
private void unlinkCancelledWaiters() {
//头结点
Node t = firstWaiter;
Node trail = null;
while (t != null) {
//头结点的下一个节点
Node next = t.nextWaiter;
//如果头结点的状态不是-2 需要出队
if (t.waitStatus != Node.CONDITION) {
t.nextWaiter = null;
//将头结点的下一个节点作为新的头结点
if (trail == null)
firstWaiter = next;
else
trail.nextWaiter = next;
//如果头结点的下一个节点是空 说明只有头结点
//将trail赋值给lastWaiter
if (next == null)
lastWaiter = trail;
} else{
trail = t;
}
t = next;
}
}
fullyRelease:释放锁
// ㈣ 因为某线程可能重入,需要将 state 全部释放
final int fullyRelease(Node node) {
//此处的节点是要await的节点
boolean failed = true;
try {
//注意这里释放的是多次加锁累加的state
//注意这里释放的是多次加锁累加的state
int savedState = getState();
if (release(savedState)) {
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
//这里保证了异常也会被移除
if (failed)
node.waitStatus = Node.CANCELLED;
}
}
//释放头节点
public final boolean release(int arg) {
//注意这里释放的是多次加锁累加的state
if (tryRelease(arg)) {
Node h = head;
//头结点的waitStatus可能是0
//原因是解锁的时候会用cas修改头结点的waitStatus=0
//如果是0为什么不需要唤醒呢?
// h == null 无等待队列
// h.waitStatus == 0 说明后面没有阻塞的队列,即不需要唤醒。
if (h != null && h.waitStatus != 0){
//h是头节点
//如果头结点的后置节点为空或被取消
//从队列的末尾从后往前找,找到最前面一个需要unpark的节点
//否则就唤醒头节点的下一个节点
unparkSuccessor(h);
}
//释放成功返回true
return true;
}
return false;
}
//尝试释放
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
unparkSuccessor:唤醒同步队列上的节点
private void unparkSuccessor(Node node) {
//此处的node节点为头结点
// 如果头节点状态为 小于0 尝试重置状态为 0
// 尝试将node的等待状态置为0,这样的话,后继争用线程可以有机会再尝试获取一次锁。
int ws = node.waitStatus;
if (ws < 0) {
compareAndSetWaitStatus(node, ws, 0);
}
//获取头结点的下一个节点
Node s = node.next;
//node是头结点
//如果头结点的后置节点为空或被取消
//从队列的末尾从后往前找,找到最前面一个需要unpark的节点
//否则就唤醒头节点的下一个节点
if (s == null || s.waitStatus > 0) {
s = null;
//循环遍历从 AQS 队列从后至前找到队列中最前面一个需要unpark的节点
//注意这里做了判断t不等于null且t不等于头结点且t.waitStatus <= 0
//也就是找到的节点必定是有效的
for (Node t = tail; t != null && t != node; t = t.prev)
//最后一个是有效的
//还会继续往前找倒数第二个是不是有效
if (t.waitStatus <= 0){
s = t;
}
}
//唤醒头结点的下一个节点 去获取锁
if (s != null)
//唤醒线程
LockSupport.unpark(s.thread);
}
}
isOnSyncQueue:判断是否在同步队列
//默认新创建的node就是 Node.CONDITION
final boolean isOnSyncQueue(Node node) {
//等于-2说明在condition等待队列中 不在同步队列 返回false即可
if (node.waitStatus == Node.CONDITION || node.prev == null)
return false;
// If has successor(继任者), it must be on sync queue
if (node.next != null)
return true;
//从队列尾部开始找 找到就是 true 否则就是 false
return findNodeFromTail(node);
}
//从队列尾部开始找 找到就是 true 否则就是 false
private boolean findNodeFromTail(Node node) {
Node t = tail;
for (;;) {
if (t == node)
return true;
if (t == null)
return false;
t = t.prev;
}
}
checkInterruptWhileWaiting:检查await期间是否中断
唤醒park,判断是否被打断和被打断时机
//Thread.interrupted() 返回true 说明中途被打断过
//进入判断 (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT)
//如果 transferAfterCancelledWait 返回true 说明是在signal之前被打断 返回 THROW_IE
//如果 transferAfterCancelledWait 返回false 说明是在signal之后被打断 返回 REINTERRUPT
//Thread.interrupted() 返回false 说明中途没有被打断过
//checkInterruptWhileWaiting返回0
/***
如果线程被打断:
但进入同步队列成功:
THROW_IE = -1 退出await()方法需要抛出异常,这种模式对应于中断发生在signal之前。
因为只有在signal之前才能cas将0变成-2,进入同步队列成功。
但是进入同步队列失败:
REINTERRUPT=1 退出await()方法需要自我打断,这种模式对应于中断发生在signal之后
因为只有在signal之后,cas将0变成-2必然失败,进入同步队列失败。
***/
private int checkInterruptWhileWaiting(Node node) {
return Thread.interrupted() ?
(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT)
:
0;
}
/***
如果线程被打断:
但进入同步队列成功:
THROW_IE = -1 退出await()方法需要抛出异常,这种模式对应于中断发生在signal之前。
因为只有在signal之前才能cas将0变成-2,进入同步队列成功。
但是进入同步队列失败:
REINTERRUPT=1 退出await()方法需要自我打断,这种模式对应于中断发生在signal之后
因为只有在signal之后,cas将0变成-2必然失败,进入同步队列失败。
对应的场景是线程A先调用了signal方法,然后当前节点被移到到了同步队列
然后线程B调用interrupt方法对当前节点进行打断唤醒
也就是线程A把节点挪到了同步队列 还没执行unpark
线程B打断了节点唤醒了线程 执行了 checkInterruptWhileWaiting
这个时间点很短
***/
final boolean transferAfterCancelledWait(Node node) {
//如果节点的状态使用cas由-2改为0修改成功
//那么进入同步队列并返回true 代表进入同步队列成功
if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
enq(node);
return true;
}
//如果上面的代码执行失败 返回false
while (!isOnSyncQueue(node))
Thread.yield();
return false;
}
acquireQueued:
尝试获取锁&打断模式非signal前被打断
//前提:(线程没有被打断并且被调用了signal方法被唤醒) 或者 (是在signal之后被打断)
//那么就尝试去获取锁
//如果获取不到就继续在同步队列park阻塞直到获取到锁
//如果获取到锁,并且打断模式是在signal之后
//interruptMode = REINTERRUPT;
//这里无视打断模式也是不可打断的体现
if (acquireQueued(node, savedState) && interruptMode != THROW_IE){
interruptMode = REINTERRUPT;
unlinkCancelledWaiters
//断开取消节点
private void unlinkCancelledWaiters() {
Node t = firstWaiter;
Node trail = null;
while (t != null) {
Node next = t.nextWaiter;
//线程已经被取消
static final int CANCELLED = 1;
//线程需要去被唤醒
static final int SIGNAL = -1;
//线程正在唤醒等待条件
static final int CONDITION = -2;
//线程的共享锁应该被无条件传播
static final int PROPAGATE = -3;
//不等于-2说明需要从等待队列移除
if (t.waitStatus != Node.CONDITION) {
t.nextWaiter = null;
if (trail == null)
firstWaiter = next;
else
trail.nextWaiter = next;
if (next == null)
lastWaiter = trail;
}else{
trail = t;
}
t = next;
}
}
reportInterruptAfterWait
// ㈤ 应用打断模式
private void reportInterruptAfterWait(int interruptMode)
throws InterruptedException {
// 打断模式 - 说明进入等待队列失败,需要自我打断
// private static final int REINTERRUPT = 1;
// 打断模式 - 说明进入等待队列成功,需要抛出异常
//private static final int THROW_IE = -1;
if (interruptMode == THROW_IE)
throw new InterruptedException();
else if (interruptMode == REINTERRUPT)
selfInterrupt();
}
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
awaitNanos(long nanosTimeout)
// 等待 - 直到被唤醒或打断或超时
public final long awaitNanos(long nanosTimeout) throws InterruptedException {
if (Thread.interrupted()) {
throw new InterruptedException();
}
// 添加一个 Node 至等待队列, 见 ㈠
Node node = addConditionWaiter();
// 释放节点持有的锁
int savedState = fullyRelease(node);
// 获得最后期限
final long deadline = System.nanoTime() + nanosTimeout;
int interruptMode = 0;
// 如果该节点还没有转移至 AQS 队列, 阻塞
while (!isOnSyncQueue(node)) {
// 已超时, 退出等待队列
if (nanosTimeout <= 0L) {
transferAfterCancelledWait(node);
break;
}
// park 阻塞一定时间, spinForTimeoutThreshold 为 1000 ns
if (nanosTimeout >= spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
// 如果被打断, 退出等待队列
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
nanosTimeout = deadline - System.nanoTime();
}
// 退出等待队列后, 还需要获得 AQS 队列的锁
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
// 所有已取消的 Node 从队列链表删除, 见 ㈡
if (node.nextWaiter != null)
unlinkCancelledWaiters();
// 应用打断模式, 见 ㈤
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
return deadline - System.nanoTime();
}
// 等待 - 直到被唤醒或打断或超时, 逻辑类似于 awaitNanos
public final boolean awaitUntil(Date deadline) throws InterruptedException {
// ...
}
// 等待 - 直到被唤醒或打断或超时, 逻辑类似于 awaitNanos
public final boolean await(long time, TimeUnit unit) throws InterruptedException {
// ...
}
// 工具方法 省略 ...
1.进入await队列
2.释放锁
3.唤醒同步队列的头节点的下一个节点。如果头结点的下一个节点不为空且没有取消,unpark该节点。
否则从尾节点开始找,从后往前找最前面一个是-1状态的节点。
signal源码
// 唤醒 - 必须持有锁才能唤醒, 因此 doSignal 内无需考虑加锁
public final void signal() {
//判断当前线程是否是持有锁的线程 不是就报错,这也解释了为什么调用signal为什么要持有锁
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
//获取头结点
Node first = firstWaiter;
if (first != null){
//唤醒
doSignal(first);
}
}
// 唤醒 - 将没取消的第一个节点转移至AQS同步队列
private void doSignal(Node first) {
//first是头结点firstWaiter
do {
// 如果头结点下一个节点是null 说明目前只有1个节点
// 将尾节点置为null
//firstWaiter是first的下一个节点
if ( (firstWaiter = first.nextWaiter) == null) {
lastWaiter = null;
}
//
//断开头结点的下一个节点 即 排在第二的节点
first.nextWaiter = null;
//将等待队列中的 Node 转移至 AQS 队列,
//如果转移至 AQS 队列失败且还有节点则继续向下循环 ㈢
//每次只会从等待队列移动一个节点到同步队列
//因为transferForSignal返回true代表转移成功
//first = firstWaiter 将first的下一个节点赋值给first
} while (!transferForSignal(first) &&(first = firstWaiter) != null);
}
// 外部类方法, 方便阅读, 放在此处
// ㈢ 如果节点状态是取消, 返回 false 表示转移失败, 否则转移成功
final boolean transferForSignal(Node node) {
//node是头结点
// 如果状态已经不是 Node.CONDITION=-2, 说明被取消了
// 同时这里
// 和await方法中addConditionWaiter方法设置为Node.CONDITION呼应
// 和checkInterruptWhileWaiting判断打断时机的代码呼应 因为cas -2 改为 0 只能有1个地方成功
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
// 将等待队列头结点加入同步队列尾部
Node p = enq(node);
// 上一个节点被取消 或者 上一个节点 设置状态为 Node.SIGNAL=-1 失败
int ws = p.waitStatus;
/***
下面的代码是为了进一步提升性能,针对两种情况:
如果插入node前,AQS内部等待队列的队尾节点就已经被取消,则满足wc > 0
如果插入node后,AQS内部等待队列的队尾节点已经稳定,满足tail.waitStatus == 0
但在执行ws > 0之后!compareAndSetWaitStatus(p, ws, Node.SIGNAL)之前被取消,则CAS也会失败,满足compareAndSetWaitStatus(p, ws, Node.SIGNAL) == false
这两种情况下,提前唤醒node能够在等待锁的同时,预先完成一部分ConditionObject#await()中无需同步的工作。这部分成本不能被轻易忽视,因为条件队列被应用最多的场景是高并发,大量线程累加起来的成本是很可观的。
链接:https://www.jianshu.com/p/a932c184db52
***/
节点被取消的原因:
This node is cancelled due to timeout or interrupt.
Nodes never leave this state. In particular,
a thread with cancelled node never again blocks.
//如果上一个节点被取消,或者在执行ws > 0之后
//!compareAndSetWaitStatus(p, ws, Node.SIGNAL)之前
//被取消那么就unpark当前节点的上一个节点
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL)) {
// unpark 取消阻塞, 让线程重新同步状态
LockSupport.unpark(node.thread);
}
return true;
}
// 全部唤醒 - 等待队列的所有节点转移至 AQS 队列
private void doSignalAll(Node first) {
lastWaiter = firstWaiter = null;
do {
Node next = first.nextWaiter;
first.nextWaiter = null;
transferForSignal(first);
first = next;
} while (first != null);
}
// 全部唤醒 - 必须持有锁才能唤醒, 因此 doSignalAll 内无需考虑加锁
public final void signalAll() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignalAll(first);
}
// 不可打断等待 - 直到被唤醒
public final void awaitUninterruptibly() {
// 添加一个 Node 至等待队列, 见 ㈠
Node node = addConditionWaiter();
// 释放节点持有的锁, 见 ㈣
int savedState = fullyRelease(node);
boolean interrupted = false;
// 如果该节点还没有转移至同步队列, 阻塞
while (!isOnSyncQueue(node)) {
// park 阻塞
LockSupport.park(this);
// 如果被打断, 仅设置打断状态
if (Thread.interrupted()){
interrupted = true;
}
}
// 唤醒后, 尝试竞争锁, 如果失败进入 AQS 队列
if (acquireQueued(node, savedState) || interrupted)
selfInterrupt();
}
// ㈠ 添加一个 Node 至等待队列
private Node addConditionWaiter() {
Node t = lastWaiter;
//Node.CONDITION=-2 表示结点等待在Condition上,!= Node.CONDITION代表结点没有在等待队列上
//所有已取消的 Node 从队列链表断开, 见 ㈡
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
// 创建一个关联当前线程的新Node,
//如果头部为空,那么添加至队列头部,头部尾部都指向这个新node
//如果头部不为空,那么添加到队列尾部
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
// ㈣ 因为某线程可能重入,需要将 state 全部释放
final int fullyRelease(Node node) {
//此处的节点是要await的节点
boolean failed = true;
try {
//注意这里释放的是多次加锁累加的state
int savedState = getState();
if (release(savedState)) {
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
if (failed)
node.waitStatus = Node.CANCELLED;
}
}
}