java 并行流中直接对非线程安全集合(如 arraylist)执行 `foreach` 会导致竞态条件;强行用 `synchronized` 包裹 lambda 不仅违背流式编程原则,还会严重损害性能。正确做法是使用线程安全的 `collectors.tolist()` 等内置收集器。
在 Java 中,IntStream.parallel().forEach(...) 是典型的有副作用(side-effecting)操作——它试图从并行线程中修改共享的 ArrayList 实例。而 ArrayList 本身不是线程安全的,多个线程同时调用 add() 可能导致元素丢失、索引越界,甚至 ConcurrentModificationException,因此最终 data.size() 很可能小于 100(如输出 97、92 等非确定值)。
虽然技术上可通过 synchronized 强制同步(例如 forEach(s -> synchronized(data) { data.add(s); })),但这是反模式:
✅ 正确解法是消除副作用,转为声明式收集——使用 collect() 配合线程安全的 Collector:
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class Q03 {
public static void main(String[] args) {
List data = IntStream.range(0, 100)
.parallel() // 支持并行
.boxed() // int → Integer
.collect(Collectors.toList()); // 线程安全的归约操作
System.out.println(data.size()); // 稳定输出:100
}
} Collectors.toList() 内部采用分段收集(fork-join 分治):每个线程先构建局部列表,再合并,全程无共享写冲突,天然支持并行且高效。类似地,还可选用:
⚠️ 注意事项:
w ArrayList()) 仍不能用于 parallel().forEach() —— 同步仅保护单次 add(),无法保证流操作整体原子性; 总结:不要同步 lambda,而要重构逻辑。Stream 的力量在于声明“做什么”,而非“怎么做”;让 collect() 承担并发协调职责,代码更简洁、健壮且高性能。