并行流 parallel() 复用 JVM 全局共享的 ForkJoinPool.commonPool(),默认并行度为 CPU 核数减 1;普通流 stream() 完全在当前线程顺序执行,不涉及任何线程池。

并行流的 parallel() 并不创建新线程池,而是直接复用 JVM 全局共享的 ForkJoinPool.commonPool()。普通流(stream())则完全不涉及线程池——它只在当前调用线程中顺序执行,无任何并发调度。
parallel() 的线程池来源是固定的
所有未显式指定线程池的并行流,底层都走同一个 ForkJoinPool 实例:ForkJoinPool.commonPool()。这不是每次调用 new 出来的,而是 JVM 启动时初始化一次的静态共享池。
- 它的默认并行度 =
Runtime.getRuntime().availableProcessors() - 1(比如 8 核机器默认为 7) - 可通过系统属性提前配置:
System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "12"); - 这个设置影响所有使用 commonPool 的地方——不只是 parallelStream,还包括
ForkJoinTask.invoke()、CompletableFuture默认异步等
普通流 stream() 完全不依赖线程池
list.stream().filter(...).map(...) 整个链路都在当前线程内完成,没有任务提交、没有工作窃取、没有线程调度开销。它和 for 循环一样,是纯同步、单线程、可预测的执行模型。
- 不会触发任何 ForkJoinPool 初始化
- 不会占用 commonPool 的线程或队列资源
- 即使你在多线程环境中调用它,每个线程各自执行自己的 stream,彼此隔离
如何验证是否共用同一个 commonPool
可以打印线程名或检查池状态来确认:
- 在 parallelStream 操作中打印线程名:
.forEach(x -> System.out.println(Thread.currentThread().getName()))
你会看到类似ForkJoinPool.commonPool-worker-1的名称 - 对比调用
ForkJoinPool.commonPool().toString()和list.parallelStream().count()前后,其 activeThreadCount / poolSize 等值会变化 - 多个并行流同时运行时,它们的子任务会竞争同一组 worker 线程,而非各自独占
注意:sequential() 不会切换线程池
调用 .parallel().sequential() 只是让后续操作退回到单线程顺序执行模式,但整个流仍可能已在 commonPool 中启动过 fork/join 分支。它不释放线程,也不切换到其他池——只是“不再并行调度”,实际执行线程仍可能是 commonPool 的某个 worker。


















