Java并行流线程安全核心是规避共享写:优先用collect而非forEach+外部集合;多输出用flatMap;自定义聚合应实现Collector接口;必要时才用AtomicInteger或ConcurrentHashMap等线程安全类型。

Java 大数据处理中用并行流,线程安全不是“加个锁就完事”,而是要从设计源头规避共享写。核心就一条:别让多个线程同时往同一个非线程安全容器里塞数据。
优先用 collect,别碰 forEach + 外部集合
这是最常见也最危险的坑。比如把 ArrayList 当作结果容器,在 parallelStream().forEach() 里反复 add,会丢数据、抛 ConcurrentModificationException 甚至 ArrayIndexOutOfBoundsException。
- ✅ 正确做法:交给 collect 统一收口,它内部已为并行做了分段收集+合并,天然线程安全
list.parallelStream().map(String::toUpperCase).collect(Collectors.toList()) - ❌ 错误写法:自己 new ArrayList,再 forEach 里 add
Listresult = new ArrayList(); list.parallelStream().forEach(s -> result.add(...));
需要多输出?用 flatMap 统一展开
一个输入生成多个结果(如拆分字符串、解析 JSON 数组),千万别在 forEach 里多次 add。这不仅线程不安全,还破坏了流式语义。
- ✅ 正确方式:用 flatMap 把每个输入转成子流,再整体 collect
.flatMap(s -> Stream.of(s + "_a", s + "_b")) .collect(Collectors.toList()) - ⚠️ 注意:flatMap 返回的是 Stream,不是 void;整个链路无共享变量、无副作用
真要自定义聚合逻辑?实现 Collector 接口
内置收集器不够用(比如要按自然顺序去重、或聚合进 TreeMap),别退化成 synchronized 块或 Collections.synchronizedList——那等于废掉并行。
立即学习“Java免费学习笔记(深入)”;
- ✅ 正确定义 Collector:
Supplier 提供每个线程的局部容器(如 () -> new TreeSet())
BiConsumer 定义如何累加到局部容器
BinaryOperator 定义如何合并两个局部容器 - ? 优势:Collector 自动完成分段、并发执行、结果合并,全程无锁无竞争
实在绕不开外部状态?选对线程安全类型
极少数场景必须累积全局状态(如计数、汇总统计),可选用原子类或并发容器,但得清楚代价:
- 计数/求和:用 AtomicInteger、AtomicLong,轻量且高效
- 集合写入:ConcurrentHashMap 适合高频读写;CopyOnWriteArrayList 只适合读多写少,频繁 add 会严重拖慢性能
- ? 避免:Vector、Collections.synchronizedList —— 所有操作串行化,基本抵消并行收益


















