怎么区分 Stream 流的并行处理 parallel() 与普通处理在底层线程池(ForkJoinPool)的共用
怎么区分 Stream 流的并行处理 parallel() 与普通处理在底层线程池(ForkJoinPool)的共用

免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈
简单来说,并行流的 parallel() 并不创建新线程池,而是直接复用 JVM 全局共享的 ForkJoinPool.commonPool()。普通流(stream())则完全是另一回事——它只在当前调用线程中顺序执行,根本不涉及任何线程池和并发调度。
parallel() 的线程池来源是固定的
所有未显式指定线程池的并行流,底层都走同一个 ForkJoinPool 实例:ForkJoinPool.commonPool()。这里有个关键点:这个池子不是每次调用时新建的,而是 JVM 启动时初始化一次的静态共享池。
- 它的默认并行度计算公式是:
Runtime.getRuntime().a vailableProcessors() - 1。举个例子,在一台8核的机器上,默认就是7个并行线程。 - 这个默认值是可以提前配置的,通过系统属性就能调整:
System.setProperty("ja va.util.concurrent.ForkJoinPool.common.parallelism", "12"); - 需要注意的是,这个设置是全局性的。它影响的不仅仅是 parallelStream,所有使用 commonPool 的地方都会生效,比如
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。
相关攻略
怎么区分 Stream 流的并行处理 parallel() 与普通处理在底层线程池(ForkJoinPool)的共用 简单来说,并行流的 parallel() 并不创建新线程池,而是直接复用 JVM 全局共享的 ForkJoinPool commonPool()。普通流(stream())则完全是另
怎么利用 IntStream summaryStatistics() 一次性获取整数序列的均值、极值与总和 在Ja va的流式编程中,IntStream summaryStatistics() 方法堪称一个“统计多面手”。它返回一个包含计数、总和、最小值、最大值和平均值的对象。这里有个关键细节:对于
11 月 11 日消息,海盗船旗下 Elgato 德国当地时间 10 日宣布推出 Discord 特别版 Stream Deck Mini。这款拥有六个可自定义 LCD 按键的直播控台采用了 Di
热门专题
热门推荐
MongoDB 3 6旧版本如何平滑迁移GridFS数据 在MongoDB 3 6版本中,使用mongodump进行数据备份时,默认会忽略GridFS存储所使用的fs files和fs chunks集合,因为它们被系统视为内部命名空间。为确保GridFS文件数据的完整迁移,必须显式指定导出这两个集合
生产环境禁用 KEYS+DEL,因其会阻塞 Redis 主线程;应使用带游标和分批的 SCAN+DEL Lua 脚本或 Ja va 中通过 RedisConnection 执行 SCAN 迭代删除,避免连接泄漏。 直接使用 KEYS 配合 DEL 来批量删除特定前缀的 Key,听起来很直接,对吧?但
Redis为什么会出现内存泄漏的假象?排查Lua脚本中未设置过期的临时变量 Redis内存持续上涨可能源于Lua脚本中未设置过期时间的临时键,如set、hset、zadd写入后遗漏expire,导致“孤儿键”累积;需用redis-cli --scan结合object freq和ttl定位,并按业务语
多级分组排名应选rank()或dense_rank()而非row_number():rank()跳过重复名次,dense_rank()连续编号;必须配合PARTITION BY和ORDER BY,且WHERE筛选需用子查询避免破坏分组。 rank() 和 dense_rank() 在多级分组中行为差
Redis如何实现基于发布订阅的配置热更新 Redis Pub Sub 能否可靠用于配置热更新? 直接拿来用?恐怕不行。Redis 的 PUBLISH SUBSCRIBE 本质上是一种“即发即弃”的模型:消息不持久、没有确认机制、订阅者离线期间的消息会彻底丢失。想象一下,你的服务因为重启或者网络短暂





