把之前CompletableFuture留下的坑给填上。 (下)

简介: 把之前CompletableFuture留下的坑给填上。 (下)

image.png


再说说 get 方法


最后,再看看 get 方法吧。之前发布的《看完JDK并发包源码的这个性能问题,我惊了!》这篇文章,有朋友看了之后有几个问题,我再串起来讲一下。

CompletableFuture 提交任务的方式有两种:

image.png

一种是 supplyAsync 带返回值的。

一种是 runAsync 返回值是 void 的,相当于没有返回值。

比如,我们用 supplyAsync 的时候:

image.png

就刻意返回一个 null。

我还可以扩展一下,假设我们的方法用的是 runAsync,本来就没有返回值的。

比如这样:

image.png

你看这里的判断条件是 (r = result) == null

那么问题就来了,假设这个方法的返回值本来就是 null,也就是我们上面的情况,怎么办呢?

为 null 就有三种情况了:

  • 1.是 runAsync 这种,真的没有返回值,所以就算任务执行完成了,get 出来的确实就是 null。
  • 2.是有返回值的,只是目前任务还没执行完成,所以 result 还是 null。
  • 3.是有返回值的,返回的值就是 null。

怎么去分别出这三种情况呢?

那么就要看看这个 result 赋值的地方了,用脚指头猜也知道在这里搞了一些事情。

所以简单的找寻一番之后,可以找到这个关键的地方:

image.png

框起来的代码,目的是为了获取 CompletableFuture 类中的 result 字段的偏移量,并用大写的 RESULT 存储起来。

有经验的朋友看到这里大概就知道它要用 compareAndSwapObject 这个骚操作了:

image.png


image.png

答案就是我框起来的部分:在 CompletableFuture 里面,把 null 也封装到 AltResult 对象里面了。

基于此,可以区分出前面我说的那三种情况。

你看这里有一个专门的 completeNull 方法,其中的调用者就有 AysncRun 方法:

image.png

再去看看调用栈,调试一下,你就知道 runAsync 这种,真的没有返回值的是怎么处理的了。

核心技术就是把 null 封装到 AltResult 对象里面。

然后如何分別返回值就是 null 的情况呢?

都有一个代表 null 的对象了,那还不简单吗,一个小小的判断就搞定了:

image.png

最后,再提一下这个方法:

java.util.concurrent.CompletableFuture#waitingGet

我之前那篇文章里面写了这样一句话:

加入这个自旋,是为了稍晚一点执行后续逻辑中的 park 代码,这个稍重一点的操作。但是我觉得这个 “brief spin-wait” 的收益其实是微乎其微的。

有小伙伴问我 park 的逻辑在哪?

其实就在 waitingGet 的 while 循环的最后一个分支里面,也就是我框起来的部分:

image.png

最后你顺着往下 Debug ,就能找到这个地方:

java.util.concurrent.CompletableFuture.Signaller#block

这里不就是 park 的逻辑吗:

image.png

打上断点自己玩去吧。

其实还有一种骚操作,我一般不告诉别人,也简单的分享一下吧。

还是拿前面的代码做演示,这个代码你跑起来之后,主线程由于调用了 get 方法,那么势必会阻塞等待异步任务的结果:

image.png

image.png

主线程是 park 起来的,在哪被 park 起来的呢?

at java.util.concurrent.CompletableFuture$Signaller.block(CompletableFuture.java:1707)


image.png

这不就是我刚刚给你说的方法吗?

然后你在这里打上断点,看一下调用堆栈,不就把主链路玩得明明白白的嘛:


image.png

怎么样,这波逆向操作,溜不溜,分分钟就学会了。


image.png

找到了 park 的地方,那么在哪儿被 unpark 的呢?

这还不简单吗?

反正我一搜就搜出来了:

image.png

然后再在 unpark 这里打上一个断点:

image.png

唤醒流程也可以调试的明明白白。

好了,挂起和唤醒都给你定位到关键地方了,就到这,玩去吧。

本文已收录自个人博客,欢迎大家来玩。

https://www.whywhy.vip/

目录
相关文章
|
5月前
|
Java
Java面试挂在线程创建后续,不要再被八股文误导了!创建线程的方式只有1种
Java面试挂在线程创建后续,不要再被八股文误导了!创建线程的方式只有1种
46 1
|
11月前
|
Java 测试技术
Java8 异步非阻塞做法:CompletableFuture 两万字详解
CompletableFuture实现了CompletionStage接口和Future接口,前者是对后者的一个扩展,增加了异步回调、流式处理、多个Future组合处理的能力,使Java在处理多任务的协同工作时更加顺畅便利
|
消息中间件 存储 Java
一网打尽异步神器CompletableFuture
最近一直畅游在RocketMQ的源码中,发现在RocketMQ中很多地方都使用到了CompletableFuture,所以今天就跟大家来聊一聊JDK1.8提供的异步神器CompletableFuture,并且最后会结合RocketMQ源码分析一下CompletableFuture的使用。
|
Java
异步利刃CompletableFuture
异步利刃CompletableFuture
96 0
|
Java 开发者 Sentinel
CompletableFuture学习整理
整理的原因是在Sentinel源码中,我们可以看到很多关于CompletableFuture的thenCompose的源码。同时在业务系统里面也看到别人写过类似的代码。因此整理了一下关于CompletableFuture使用的相关类型和特性,在处理复杂耗时业务时可以选择组合使用。
203 0
CompletableFuture学习整理
|
存储 缓存 Java
Java线程池笔记
总结一下Java线程池相关八股文
210 0
|
Java Unix 编译器
看完这篇 final、finally 和 finalize 和面试官扯皮就没问题了(二)
final 是 Java 中的关键字,它也是 Java 中很重要的一个关键字,final 修饰的类、方法、变量有不同的含义;finally 也是一个关键字,不过我们可以使用 finally 和其他关键字结合做一些组合操作;finalize 是一个不让人待见的方法,它是对象祖宗 Object 中的一个方法,finalize 机制现在已经不推荐使用了。本篇文章,cxuan 就带你从这三个关键字入手,带你从用法、应用、原理的角度带你深入浅出理解这三个关键字。
157 0
看完这篇 final、finally 和 finalize 和面试官扯皮就没问题了(二)
|
Java 编译器 程序员
看完这篇 final、finally 和 finalize 和面试官扯皮就没问题了(一)
final 是 Java 中的关键字,它也是 Java 中很重要的一个关键字,final 修饰的类、方法、变量有不同的含义;finally 也是一个关键字,不过我们可以使用 finally 和其他关键字结合做一些组合操作;finalize 是一个不让人待见的方法,它是对象祖宗 Object 中的一个方法,finalize 机制现在已经不推荐使用了。本篇文章,cxuan 就带你从这三个关键字入手,带你从用法、应用、原理的角度带你深入浅出理解这三个关键字。
107 0
看完这篇 final、finally 和 finalize 和面试官扯皮就没问题了(一)
|
Java API
把之前CompletableFuture留下的坑给填上。 (中)
把之前CompletableFuture留下的坑给填上。 (中)
239 0
把之前CompletableFuture留下的坑给填上。 (中)
|
Java 程序员
把之前CompletableFuture留下的坑给填上。 (上)
把之前CompletableFuture留下的坑给填上。 (上)
463 0
把之前CompletableFuture留下的坑给填上。 (上)