并行流中避免线程安全问题的关键是避免共享可变状态,应使用reduce、AtomicInteger等线程安全操作,禁止在forEach中修改外部变量或原始集合,优先选择无状态操作和高效可拆分的数据源。

在并行流中避免线程安全问题,核心是不让多个线程同时读写同一块非线程安全的共享状态。Java 并行流基于 ForkJoinPool 自动分发任务,但不会自动帮你加锁或同步——它只负责“并行”,不负责“安全”。所以关键在于写法设计,而非依赖框架兜底。
别改外部变量,用归约代替累加
直接修改外部数组、基本类型包装类或普通对象字段,必然引发竞态条件。比如用 int[] sum = {0} 或 Integer sum = 0 在 forEach 中累加,结果不可靠。
- ✅ 正确做法:用 reduce,它天然线程安全且语义清晰
numbers.parallelStream().reduce(0, Integer::sum) - ✅ 替代方案:用 AtomicInteger 或 LongAdder(高并发场景下比 synchronized 更高效)
AtomicInteger sum = new AtomicInteger(); numbers.parallelStream().forEach(sum::addAndGet)
别操作原始集合,先收集再处理
在 parallelStream().forEach() 里调用 list.remove()、map.put() 或 set.add(),极大概率触发 ConcurrentModificationException 或数据丢失。
- ✅ 安全做法:把中间结果先 collect 到新容器,再统一操作原集合
ListtoRemove = list.parallelStream().filter(...).collect(Collectors.toList()); list.removeAll(toRemove); - ✅ 若必须边遍历边更新,改用线程安全集合(如 Collections.synchronizedList),但注意迭代时仍需手动同步
优先选无状态操作,慎用有状态中间操作
像 sorted()、distinct()、limit() 这类操作需要跨分片协调状态,并行流内部要做合并和去重,不仅慢,还可能因分片策略导致行为偏差(比如 limit(10) 在并行下未必返回前10个)。
立即学习“Java免费学习笔记(深入)”;
- ✅ 推荐:过滤、映射、归约这类无状态操作天然适合并行
- ✅ 替代思路:对大数据集,可先用并行流做预处理(如 filter/map),再用顺序流做排序或截断
确保数据源支持高效拆分
并行流性能依赖底层 Spliterator 的拆分能力。ArrayList、数组能 O(1) 随机访问,拆分快;LinkedList、TreeSet 拆分成本高,甚至被迫转成顺序执行。
- ✅ 尽量用 ArrayList 或普通数组作为源头
- ❌ 避免对 Stream.generate()、IntStream.range() 等无限或非集合来源盲目调用 parallelStream()


















