java并行流需避免并发修改原集合、使用无状态归约或线程安全容器、慎用有状态操作、按任务类型调整并行度。

Java并行流(parallelStream())用起来很顺手,但稍不注意就会掉进并发修改和线程安全的坑里——结果错、异常抛、性能反而更差。关键不是“能不能用”,而是“怎么用才不出错”。
别在并行流里改原始集合
并行流会把源集合分片,多个线程同时读取不同段。如果在 forEach 或其他操作里直接调用 list.add()、map.put() 这类方法,轻则元素丢失、重复,重则抛 ConcurrentModificationException。
- ❌ 错误写法:
list.parallelStream().forEach(x -> originalList.add(transform(x))); - ✅ 正确做法:用
collect(Collectors.toList())或toList()(Java 16+)生成新集合,不碰原数据 - ⚠️ 注意:即使用了
synchronizedList,也不推荐在并行流中直接修改——同步锁会串行化执行,失去并行意义
累加统计必须无状态或线程安全
用外部变量(比如 int sum = 0 或 ArrayList<string> results</string>)收集结果,是线程安全的高危操作。多个线程同时读写同一内存地址,结果不可预测。
- ✅ 推荐归约:
int total = list.parallelStream().reduce(0, Integer::sum); - ✅ 线程安全计数器:
AtomicInteger counter = new AtomicInteger(); list.parallelStream().forEach(x -> counter.addAndGet(x)); - ✅ 收集到线程安全容器:
CopyOnWriteArrayList<string> safeList = new CopyOnWriteArrayList(); list.parallelStream().forEach(safeList::add);</string>(仅适合读多写少场景)
慎用有状态中间操作
sorted()、distinct()、limit() 这些操作依赖全局顺序或全局去重逻辑,并行执行时需跨分片协调,开销大且可能行为异常(如 distinct() 在分片后无法保证全集唯一)。
- ? 建议:先用
collect(Collectors.toList())转成普通集合,再对结果做排序或去重 - ? 替代方案:对大数据集排序,优先考虑外部排序或数据库层处理;去重可用
Collectors.toConcurrentMap()配合键值映射 - ⚠️ 特别注意:
findFirst()在并行流中不保证返回第一个元素,它返回任意匹配项;要用确定位置的结果,就别用并行
并行度不是越高越好
默认并行度是 Runtime.getRuntime().availableProcessors() - 1,看似合理,但实际受任务类型影响极大。
- ? 计算密集型任务(如数值运算、加密解密):可接近 CPU 核心数,但不必超过
- ? I/O 密集型任务(如文件读写、远程调用):过高并行度引发频繁上下文切换,建议控制在 2–4 之间,或改用异步非阻塞方式
- ⚙️ 自定义并行池:
ForkJoinPool pool = new ForkJoinPool(4); pool.submit(() -> list.parallelStream().map(...).collect(...)).join();
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











