parallelStream仅在数据量大、单个元素处理耗时明显(如I/O或复杂计算)时才比stream快;小集合(如size

parallelStream 什么时候比 stream 快
并行流不是银弹,只有在数据量大、单个元素处理耗时明显(比如含 I/O 或复杂计算)时才可能提速。小集合(如 list.size() )用 parallelStream() 反而更慢——线程创建、任务拆分、结果合并的开销压过了并行收益。
常见误判场景:list.parallelStream().map(String::toUpperCase).collect(Collectors.toList()) 这类纯内存轻量操作,几乎总比串行慢。
- 适合:遍历万级对象,每个做 HTTP 请求、JSON 解析、正则匹配
- 不适合:遍历百级列表,只做
Integer::intValue或字符串拼接 - 验证方法:用
System.nanoTime()对比,别靠感觉
共享变量导致结果错乱的典型写法
parallelStream 默认使用 ForkJoinPool.commonPool(),所有任务共享线程,若在 lambda 中修改外部变量(尤其非线程安全容器),结果必然不可控。
Listresult = new ArrayList<>(); // 错!ArrayList 不是线程安全的 list.parallelStream() .forEach(s -> result.add(s.toUpperCase())); // 多线程并发 add → 数据丢失或异常
正确做法是用线程安全的收集方式:
立即学习“Java免费学习笔记(深入)”;
- 用
collect():list.parallelStream().map(String::toUpperCase).collect(Collectors.toList()) - 用线程安全容器 +
forEachOrdered()(但失去并行意义) - 避免副作用:lambda 内不读写外部可变状态
自定义线程池控制 parallelStream 并发度
commonPool 默认线程数 = Runtime.getRuntime().availableProcessors() - 1,对 CPU 密集型任务尚可,但遇到 I/O 操作多的任务,需要更多线程才能压满资源。
不能直接替换 parallelStream() 的执行器,得绕一下:
ForkJoinPool pool = new ForkJoinPool(8); Listresult = pool.submit(() -> list.parallelStream() .map(this::heavyCompute) .collect(Collectors.toList()) ).join();
- 必须调用
submit().join(),否则任务不执行 - 记得
pool.shutdown()(尤其在短生命周期应用中) - Spring 环境可用
@Async+ 自定义TaskExecutor替代,更可控
parallelStream 在 ArrayList 和 LinkedList 上性能差异大
parallelStream 底层依赖 Splitter 拆分数据源。只有支持随机访问(RandomAccess)的集合(如 ArrayList、数组)才能高效切分;LinkedList 拆分时需遍历定位,性能断崖式下跌,甚至不如串行。
实测:10 万元素的 LinkedList.parallelStream() 可能比 ArrayList.stream() 慢 5 倍以上。
- 始终优先用
ArrayList或数组做并行源 - 若只有
LinkedList,先new ArrayList(original)再并行 -
HashSet/TreeSet无序且不保证拆分效率,也不推荐直接并行
用 parallelStream 前先问自己:数据够多吗?操作够重吗?集合类型合适吗?外部状态干净吗?四个问题里有一个答不上,就老实用 stream()。










