aqs condition await 是怎样被唤醒的?

问题

看完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执行

image.png
执行完 fullyRelease(node); 会把 state 修改为0
image.png
isOnSyncQueue 这个方法在第一次执行的时候,都还未转移,此时while 条件是true
所以会走到 LockSupport.park(this);
之后,线程1会进入阻塞状态
image.png
image.png

线程2执行

image.png
lock.unlock 底层会调用 unparkSuccessor,用于AQS中唤醒阻塞的线程
image.png
这里选idea debug窗口选一下 线程1,发现线程1 已经被阻塞了
image.png
**执行完当前代码之后,**会立刻发现线程1,马上就唤醒了。
并且会把 lock 的state =1,让线程1再次获得锁,这个赋值操作代码是
public native void park(boolean var1, long var2);
推测是底层c或者c++去调java函数,修改state=1的
image.png

线程1执行

会发现,本来阻塞到 condition1.await(); 这里就被线程2的lock.unlock 唤醒了
直接执行到了最后一步
image.png

debug 线程(异常唤醒)

这里会有个疑问何为异常流程?signal =》doSignal =》transferForSignal
这里的 LockSupport._unpark_(node.thread);
在下面俩个条件中属于异常唤醒:

  • 1、ws>0 ,说明当前节点已被取消
  • 2、cas修改失败,说明该节点临时被取消

这里尝试debug未能触发上面俩个条件,哪位大佬指点一下
image.png
唤醒的是哪里代码呢,其实也是唤醒对应的 await while里面的park方法
image.png

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值