很多人以为 CompletableFuture 的链式回调都跑在异步线程池里。真实测两种场景,发现同一段 thenApply 代码,执行所在的线程真的会变。
上一篇:Record 类真实反编译:equals/hashCode/toString 是一条 invokedynamic,不是手写代码
这部分符合预期,先作为基准确认。
main 线程真实名字 = main supplyAsync 真实执行所在的线程 = ForkJoinPool.commonPool-worker-1 和 main 线程是同一个吗 = false
同一段 thenApply 代码,分别接在一个几乎瞬间完成的 supplyAsync 后面,和一个真实耗时 200ms 的 supplyAsync 后面。
CompletableFutureDemo.java CompletableFuture<String> chained = CompletableFuture.supplyAsync(() -> "hello") .thenApply(s -> s + " from " + Thread.currentThread().getName()); CompletableFuture<String> chainedSlow = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(200); } catch (InterruptedException ignored) {} return "hello-slow"; }).thenApply(s -> s + " from " + Thread.currentThread().getName());
thenApply 真实执行结果(supplyAsync 几乎瞬间完成) = hello from main thenApply 真实执行结果(supplyAsync 真实耗时 200ms) = hello-slow from ForkJoinPool.commonPool-worker-1
同一段回调代码,两次真实运行的执行线程完全不同——第一次真实在 main 线程,第二次真实在 ForkJoinPool.commonPool-worker-1。两次运行反复验证,结果稳定可重复。
默认线程池的并行度和真实的任务分布情况。
Runtime.availableProcessors() = 10 ForkJoinPool.commonPool() 的真实并行度 = 9 8 个 runAsync 任务真实用到的不同线程数 = 8 真实用到的线程名 = [ForkJoinPool.commonPool-worker-7, ..., ForkJoinPool.commonPool-worker-4]
真实并行度是 CPU 核数 - 1(这台机器 10 核,真实并行度 9)——JDK 默认给 commonPool 留一个核给调用线程本身和其它系统任务用。8 个真实的耗时任务分散到了 8 个不同的工作线程上并行执行。
每一行都是真实运行这段代码,打印出真实执行线程名字得到的结果。