为啥 AQS 的 CondtionObject 的 firstWaiter 不需要是 volatile 的?

2020-06-09 23:51:21 +08:00
 amiwrong123

其实关于这一点,网上大部分人都会说,要 await()必然要先 lock(),既然 lock()了就表示没有竞争,没有竞争自然也没必要使用 volatile+CAS 的机制去保证什么。所以 firstWaiter 不需要是 volatile 的。

但是实际上我感觉还是会有问题,比如你执行下面这段程序:

        final Lock lock=new ReentrantLock();
        Condition Emptycondition = lock.newCondition();
        Emptycondition.await();  //这样会抛出异常

比如你让一个子线程去做上面的事,会抛出异常,因为 await()的时候没有获取锁呢。但是在抛出异常之前,你已经执行了一部分代码了:

        public final void await() throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            Node node = addConditionWaiter();
            int savedState = fullyRelease(node);//在这里抛出的异常,意味着已经执行了 addConditionWaiter
            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);
        }

    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)//抛出异常后,failed 为真,但善后处理只是将 node 的状态变成 CANCELLED
                node.waitStatus = Node.CANCELLED;
        }
    }

再来看 addConditionWaiter 的逻辑:

        private Node addConditionWaiter() {
            Node t = lastWaiter;
            // If lastWaiter is cancelled, clean out.
            if (t != null && t.waitStatus != Node.CONDITION) {
                unlinkCancelledWaiters();
                t = lastWaiter;
            }
                 Node node = new Node(Thread.currentThread(), Node.CONDITION);
            if (t == null)
                firstWaiter = node;//对不保证可见性的变量进行赋值了
            else
                t.nextWaiter = node;
            lastWaiter = node;//对不保证可见性的变量进行赋值了
            return node;
        }

好了,现在执行完我的那三行测试代码后,Emptycondition 的条件队列上有了一个 node,而且这个 node 包装了一个死掉的线程。因为抛出异常

现在假设有另一个线程执行正常流程的代码:

        final Lock lock=new ReentrantLock();
        Condition Emptycondition = lock.newCondition();
        lock.lock();
        try{
            Emptycondition.await();  
        } finally {
            lock.unlock();
        }
        

现在条件是:

  1. 线程 A 执行 测试代码,会执行到 addConditionWaiter
  2. 线程 B 执行 正常流程代码,也会执行到 addConditionWaiter
  3. 两个线程可能同时执行 addConditionWaiter,却没有对 firstWaiter 进行 volatile+CAS 的保护,难道这样不会有问题吗?
1679 次点击
所在节点    Java
12 条回复
sagaxu
2020-06-10 00:19:53 +08:00
因为你的代码没有做到要 await()必然要先 lock()
amiwrong123
2020-06-10 00:30:23 +08:00
@sagaxu
额,我好像没懂你意思。所以你想说 firstWaiter 不加 volatile,就会有问题呗
muyunn
2020-06-10 01:10:17 +08:00
好长时间不用,差点被楼主绕进去,你必须先调用 lock.lock()获取锁,然后才可以 await
vk42
2020-06-10 01:16:50 +08:00
lz 是想说不先 lock 的情况下后果不可控?但本来没按规则先 lock 不就是 bug 吗?直接修 bug 不是更简单么
luckyrayyy
2020-06-10 02:41:50 +08:00
我的哥啊,你要研究一年 aqs ?
amiwrong123
2020-06-10 08:16:43 +08:00
@muyunn
是的,标准用法是这样
amiwrong123
2020-06-10 08:20:45 +08:00
@vk42
好吧,所以相当于决定权给了用户,作者默认使用者是按照正确用法使用的
amiwrong123
2020-06-10 08:40:25 +08:00
@luckyrayyy
哈哈哈,最近发帖太多被发现了吗。这几天 condition 的实现看完,就不看 aqs 了
yamasa
2020-06-10 15:37:10 +08:00
佩服这牛角尖精神。虽然我觉得现行环境下死磕 jdk 源码不如刷 leetcode 来的现实。
amiwrong123
2020-06-10 16:30:28 +08:00
@yamasa
哎,所以我是又努力错方向了吗 ==
lux182
2020-06-10 20:35:59 +08:00
我觉得如果你用最简洁的语言表述清楚了,可能你也就找到答案了
amiwrong123
2020-07-18 23:27:51 +08:00
给自己回复一下,我问的这个问题,其实是个 bug,详见 https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8187408

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/680184

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX