问题
看完aqs condition 源码后有个疑问,await 方法中的 LockSupport.park(this); 是在什么时候被唤醒呢?
其实看了很多博客都是互相抄,压根没人考虑这个问题,就算哪个大佬提了一嘴,也是模棱俩可。
这里不去过多解析每行代码,只去分析 await 何时被唤醒
public final void await() throws InterruptedException {
// 允许线程中断,中断直接抛出异常
if (Thread.interrupted())
throw new InterruptedException();
// 把当前线程添加到条件队列
Node node = addConditionWaiter();
/**
这里为什么全部释放可重入锁?
见下面分析,先给结论:
这里先释放当前线程1持有lock所有锁,
目的为了让线程2,执行lock.unlock 方法后,
唤醒下面执行到 LockSupport.park(this);
*/
int savedState = fullyRelease(node);
int interruptMode = 0;
// 如果没有转为阻塞队列,就会执行 LockSupport.park(this)
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);
}
详情图
先看大概详情图,再去看下面具体分析
研究
执行到 LockSupport.park(this); 先不往下面看了
这里用idea的多线程debug模式去分析这个问题
分析代码demo
public class PrintByOrder2 {
public int state = 1;
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
@SneakyThrows
public void print1() {
lock.lock();
try {
while (state != 1) {
condition1.await();
}
System.out.println(Thread.currentThread().getName() + " :print1...");
state++;
// 唤醒第二个条件队列
condition2.signal();
} finally {
lock.unlock();
}
}
@SneakyThrows
public void print2() {
lock.lock();
try {
while (state != 2) {
condition2.await();
}
System.out.println(Thread.currentThread().getName() + " :print2...");
state = 1;
// 唤醒第二个条件队列
condition1.signal();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
PrintByOrder2 printByOrder = new PrintByOrder2();
new Thread(() -> {
while (true) {
printByOrder.print1();
}
}).start();
new Thread(() -> {
while (true) {
printByOrder.print2();
}
}).start();
}
debug 线程(正常唤醒)
为了更好描述
- Thread-0 称为 线程1
- Thread-1 称为 线程2
线程1执行
执行完 fullyRelease(node); 会把 state 修改为0
isOnSyncQueue 这个方法在第一次执行的时候,都还未转移,此时while 条件是true
所以会走到 LockSupport.park(this);
之后,线程1会进入阻塞状态
线程2执行
lock.unlock 底层会调用 unparkSuccessor,用于AQS中唤醒阻塞的线程
这里选idea debug窗口选一下 线程1,发现线程1 已经被阻塞了
**执行完当前代码之后,**会立刻发现线程1,马上就唤醒了。
并且会把 lock 的state =1,让线程1再次获得锁,这个赋值操作代码是 public native void park(boolean var1, long var2);
推测是底层c或者c++去调java函数,修改state=1的
线程1执行
会发现,本来阻塞到 condition1.await(); 这里就被线程2的lock.unlock 唤醒了
直接执行到了最后一步
debug 线程(异常唤醒)
这里会有个疑问何为异常流程?signal =》doSignal =》transferForSignal
这里的 LockSupport._unpark_(node.thread);
在下面俩个条件中属于异常唤醒:
- 1、ws>0 ,说明当前节点已被取消
- 2、cas修改失败,说明该节点临时被取消
这里尝试debug未能触发上面俩个条件,哪位大佬指点一下
唤醒的是哪里代码呢,其实也是唤醒对应的 await while里面的park方法