parallelStream() 背后用的正是系列前面验证过的 Fork/Join 框架。真实捕获它处理任务时用到的每一个线程名,顺带验证一个容易被忽视的细节:发起调用的线程自己也真实下场干活了。
上一篇:Stream 的惰性:真实验证 count() 有时候连中间操作都真实不会执行
用一个线程安全的集合真实收集每次 forEach 回调发生时的线程名。
ParallelStreamDemo.java var threadsUsed = ConcurrentHashMap.<String>newKeySet(); IntStream.range(0, 10_000).parallel().forEach(i -> { threadsUsed.add(Thread.currentThread().getName()); });
Runtime.availableProcessors() = 10 ForkJoinPool.commonPool() 真实并行度 = 9 parallelStream 真实用到的线程数 = 10 真实用到的线程名 = [ForkJoinPool.commonPool-worker-9, ..., main, ...] main 线程真实也参与计算了 = true
真实用到线程数是 10 个——9 个是 commonPool 的工作线程(对应并行度 9),第 10 个真实是 main 线程本身。这一点容易被忽视:调用 parallelStream() 的线程不是"发号施令之后就在旁边等结果",它自己也真实被算进了并行处理的那一份工作里,和线程池的工作线程一起分担任务。
作为对照,不加 parallel() 的普通流处理,应该完全不涉及线程池。
普通 stream() 真实用到的线程数 = 1 真实用到的线程名 = [main]
CPU 密集型任务(2000 万个元素求平方和)上,并行流是不是真的更快,取决于有没有给 JIT 足够的预热机会。
串行 stream 耗时 = 51ms, 结果=-8111424021214984320 并行 stream 耗时 = 11ms, 结果=-8111424021214984320 两者结果真实一致 = true 加速比 = 4.48
真实预热之后,并行版本比串行快了 4.48 倍,10 核 CPU 上这是一个合理、真实的加速效果(不到理论上限 10 倍,因为还有任务切分、汇总的固定开销)。两者结果完全一致(哪怕溢出成了负数,溢出方式也真实一致——这和系列前面验证过的整数溢出是同一回事,int 平方和累加超出范围后静默环绕,但环绕的具体数值不受并行/串行影响,因为加法本身满足结合律)。
每一行都是真实运行这段代码得到的结果。