Doug Lea在J.U.C包里面写的BUG又被网友发现了(3)

简介: Doug Lea在J.U.C包里面写的BUG又被网友发现了(3)

另外,我觉得这个 BUG 的提交者自己应该解释我们为什么需要修改这部分代码。


其实 Doug 的言外之意就是:你说这部分有问题,你给我举个例子,别只是整理论的,你弄点代码给我看看。


image.png


他说:哦,原来是故意的呀。


这句话,你用不同的语气可以读出不同的含义。


我这里倾向于他觉得既然 Doug 当初写这段代码的时候考虑到了这点,他分析之后觉得自己这样写是没有问题的,就这样写了。


好嘛,前面说 INTERRUPING 不需要特殊处理,现在说 COMPLETING 状态是检测不到的。


那就没得玩了。


image.png


事情现在看起来已经是被定性了,那就是不需要进行修改。


但是就在这时 Paul 同学杀了个回马枪,应该也是前面的讨论激发了他的思路,你不是说检测不出来吗,你不是说 get 方法可以获得最终的正确结果吗?


那你看看我这段代码是什么情况:


image.png


代码是这样的,大家可以直接粘贴出来,在 JDK 8/9 环境下分别运行一下:


public static void main(String[] args) throws Exception {
        AtomicReference<FutureTask<Integer>> a = new AtomicReference<>();
        Runnable task = () -> {
            while (true) {
                FutureTask<Integer> f = new FutureTask<>(() -> 1);
                a.set(f);
                f.run();
            }
        };
        Supplier<Runnable> observe = () -> () -> {
            while (a.get() == null);
            int c = 0;
            int ic = 0;
            while (true) {
                c++;
                FutureTask<Integer> f = a.get();
                while (!f.isDone()) {}
                try {
                    /*
                    Set the interrupt flag of this thread.
                    The future reports it is done but in some cases a call to
                    "get" will result in an underlying call to "awaitDone" if
                    the state is observed to be completing.
                    "awaitDone" checks if the thread is interrupted and if so
                    throws an InterruptedException.
                     */
                    Thread.currentThread().interrupt();
                    f.get();
                }
                catch (ExecutionException e) {
                    throw new RuntimeException(e);
                }
                catch (InterruptedException e) {
                    ic ++;
                    System.out.println("InterruptedException observed when isDone() == true " + c + " " + ic + " " + Thread.currentThread());
                }
            }
        };
        CompletableFuture.runAsync(task);
        Stream.generate(observe::get)
                .limit(Runtime.getRuntime().availableProcessors() - 1)
                .forEach(CompletableFuture::runAsync);
        Thread.sleep(1000);
        System.exit(0);
    }


先看一下这段代码的核心逻辑:


微信图片_20220426225907.png


首先标号为 ① 的地方是两个计数器,c 代表的是第一个 while 循环的次数,ic 代表的是抛出 InterruptedException(IE) 的次数。


标号为 ② 的地方是判断当前任务是否是完成状态,如果是,则继续往下。


标号为 ③ 的地方是先中断当前线程,然后调用 get 方法获取任务结果。


标号为 ④ 的地方是如果 get 方法抛出了 IE 异常,则在这里进行记录,打印日志。


需要注意的是,如果打印日志了,说明了一个问题:


前面明明 isDone 方法返回 true 了,说明方法执行完成了。但是我调用 get 方法的时候却抛出了 IE 异常?


这你怕是有点说不通吧!


JDK 8 的运行结果我给大家截个图。


image.png


这个异常是在哪里被抛出来的呢?


awaitDone 方法的入口处,就先检查了当前线程是否被中断,如果被中断了,那么抛出 IE 异常:


image.png


任务状态是小于等于 COMPLETING 的时候。


在示例代码中,前面的 while 循环中的 isDone 方法已经返回了 true,说明当前状态肯定不是 NEW。


那么只剩下个什么东西了?


就只有一个 COMPLETING 状态了。


小样,这不就是监测到了吗?


image.png


在这段示例代码出来后的第 8 个小时,David 靓仔又来说话了:


image.png


他要表达的意思,我理解的是这样的:


在 j.u.c 包里面,优先检查线程中断状态是很常见的操作,因为相对来说,会导致线程中断的地方非常的少。


但是不能因为少,我们就不检查了。


我们还是得对其进行了一个优先检查,告知程序当前线程是否发生了中断,即是否有继续往下执行的意义。


但是,在这个场景中,当前线程中断了,但并不能表示 Future 里面的 task 任务的完成情况。这是两个不相关的事情。


即使当前线程中断了,但是 task 任务仍然可以继续完成。但是执行 get 方法的线程被中断了,所以可能会抛出 InterruptedException。


因此,他给出的解决建议是:


可以选择优先返回结果,在 awaitDone 方法的循环中把检查中断的代码挪到后面去。


五天之后,之前 BUG 的提交者 Martin 同学又来了:


image.png

目录
相关文章
|
8月前
|
Java 容器
阿里内部流传的JDK源码剖析手册!GitHub已获上千万的访问量
相信现在已经有很多小伙伴知道了“微软”要对JDK下手了! JDK是什么? jdk是Java语言的软件开发工具包,主要用于移动设备、嵌入式设备上的java应用程序。jdk是整个java开发的核心,它包含了JAVA的运行环境和JAVA工具。相对而言,没有jdk的话,无法编译Java程序(指java源码.java文件),如果想只运行Java程序(指class或jar或其它归档文件),要确保已安装相应的JRE。
203 0
|
5月前
|
NoSQL Java Redis
阿里P8熬了一个月肝出这份32W字Java面试手册,在Github标星31K+
互联网行业竞争越来越严峻,面试也是越来越难,一直以来我都想整理一套完美的面试宝典,奈何难抽出时间,这套1000+道的Java面试手册我整理了整整1个月,上传到Git上目前star数达到了30K+
|
5月前
|
Java 容器
膜拜!清华大佬手撸多线程并发源码笔记Github上线3天星标35k+
你为什么要学习多线程?是因为理想吗?是因为热爱吗? 哦~原来是为了面试打基础、做准备啊!没错,这真的很现实!
膜拜!清华大佬手撸多线程并发源码笔记Github上线3天星标35k+
|
6月前
|
消息中间件 Dubbo Java
GitHub标星翻倍!阿里大牛呕心沥血终成39w字Java面试笔记
好不容易有个大厂面试机会,面试官才问了两三个问题,就已经回答不上来的,只想找个地缝钻进去,连进入技术面的机会都没有,现在大厂都在大量招聘Java工程师,但面试题怎么都这么难?!
|
8月前
|
消息中间件 Java 程序员
GitHub和 Gitee联合编写最新版20w字Java全栈面试手册,简直无敌!
最近小编发现了一份牛逼的Java全栈面试手册,这份面试手册深入到面试官和面试者的角度还原了真实的面试场景对话! 而且还是程序员两大面试巨头平台GitHub和 Gitee联手编写的,其内容可以说是在全网所有面试题中都“首屈一指”内容非常详细很多细节都给大家做了图和怎么应对面试官的问题!
GitHub和 Gitee联合编写最新版20w字Java全栈面试手册,简直无敌!
|
8月前
|
设计模式 Dubbo Java
让GitHub低头认错的这份阿里内部绝密Java面试八股文手册有多强?
今天给大家分享出一份让大家上班摸鱼也可以随意看的阿里巴巴内部特供Java面试八股文手册,这份手册据说曾经也是让GitHub都为之低头的存在! 正所谓“旧时王谢堂前燕,飞入寻常百姓家”希望大家看完这份手册都可以拿上自己满意的offer!
|
9月前
|
消息中间件 设计模式 Java
解决90%面试问题!GitHub顶级"Java面试手册"了解下八股文天花板
身为java开发工程师的你找到自己满意的工作了吗?又或者还在面试的路上经历一次又一次的失败。迟迟找不到正确的开门砖,也许你的技术能力可能并不差但就是在面试上得不到充分的证明。
|
12月前
|
存储 Rust 供应链
编写完10万行代码,我发了篇长文吐槽Rust
编写完10万行代码,我发了篇长文吐槽Rust
133 0
|
移动开发 Java 容器
这份github上被14万人点赞的Java教程太强了
这份github上被14万人点赞的Java教程太强了
185 0
这份github上被14万人点赞的Java教程太强了
|
数据可视化 开发工具 git