【问题标题】:java intstream parallel loop omitting datajava intstream并行循环省略数据
【发布时间】:2025-11-21 10:30:01
【问题描述】:

我有这段代码:

ArrayList<ArrayList<Double> results = new ArrayList<ArrayList<Double>();
IntStream.range(0, 100).parallel().forEach(x ->{
    for (int y = 0; y <100;y++){
        for (int z = 0; z <100;z++){
            for (int q = 0; q <100;q++){
                results.add(someMethodThatReturnsArrayListDouble);
            }
        }
    }
});

System.out.println(results.size());

运行此代码后,我总是得到不同的results.size(),总是短一些。知道为什么会这样以及如何解决吗?

【问题讨论】:

  • ArrayList 不是线程安全的。
  • 您应该阅读the documentation...中有关副作用的部分...

标签: java parallel-processing java-stream


【解决方案1】:

ArrayList 不是线程安全的。如果您尝试在不同的线程中向其中添加项目(这是并行化流所做的),它很可能会中断。

来自docs

请注意,此实现不同步。如果多个线程同时访问一个 ArrayList 实例,并且至少有一个线程在结构上修改了列表,则必须在外部进行同步。 (结构修改是添加或删除一个或多个元素,或显式调整后备数组大小的任何操作;仅设置元素的值不是结构修改。)这通常通过同步一些自然封装的对象来完成列表。如果不存在这样的对象,则应使用 Collections.synchronizedList 方法“包装”该列表。

在这种情况下,最简单的解决方法是删除对 parallel() 的调用。

【讨论】:

    【解决方案2】:

    您的结果未同步。有多种方法可以解决您的问题,最好的方法是让 java 流 api 处理列表的组合。

    List<List<Double>> results = IntStream.range(0, 100).parallel().flatmap(x ->{
            List<Double>> results = new ArrayList<Double>();
            for (int y = 0; y <100;y++){
                for (int z = 0; z <100;z++){
                    for (int q = 0; q <100;q++){
    
                        results.add(someMethodThatReturnsArrayListDouble);
    
                    }
                }
            }
            return results.stream();
       }).collect(Collectors.toList());
    

    这会收集方法中的列表,并将它们作为流返回,以便在方法结束时使用 collectors.toList() 进行组合,这是线程安全的。

    【讨论】:

    • 谢谢,我一定会试试的。在我去研究 doc 和其他东西之前,如果我想用 y 更改 x,你能重写代码吗? x 的循环将是经典的,而 Y 循环将是 IntStream,我很困惑将收集器放在哪里
    【解决方案3】:

    使用 Vector

    它是 List 的线程安全实现。

    【讨论】: