为什么 list.parallelStream().forEach() 不处理 Java 中列表中的所有元素?

Pra*_*Kvn 9 java parallel-processing java-stream

以下代码在完成并行处理后并未将所有元素放入目标列表中。这有什么原因吗?

public static void main(String[] args) {

    List<Integer> source =new ArrayList<>();
    List<Integer> destination = new ArrayList<>();
    IntStream.range(0, 10000).forEach(element ->{
        source.add(element);
    });

    //System.out.println(source.size());

    source.parallelStream().forEach(c->{
        destination.add(c);
    });

    System.out.println("Source Size:"+source.size());
    System.out.println("destination size:"+destination.size());
}
Run Code Online (Sandbox Code Playgroud)

输出:源大小:10000 目标大小:4343

Sle*_*idi 6

因为ArrayList不是线程安全的集合。使用类似线程安全的集合CopyOnWriteArrayList会使其正确但不一定有效。

使用 aCollector会更简单和正确。例如

source.parallelStream().collect(Collectors.toList())
Run Code Online (Sandbox Code Playgroud)

  • 或者只是 `List&lt;Integer&gt; destination = new ArrayList&lt;&gt;(source);` (2认同)

Era*_*ran 5

并行流的操作forEach是将元素从多个线程添加到非同步Collection(an ArrayList)。因此,该操作不是线程安全的,并且会产生意想不到的结果。

使用forEachOrdered()代替forEach()将确保 的所有元素都source List添加到destination List.

然而,正如另一个答案中提到的,使用是从 acollect(Collectors.toList())生成输出的正确方法。ListStream

  • `forEachOrdered` 仅保证元素按照相同的遇到顺序进行处理。它不能保证线程安全 (3认同)
  • @FedericoPeraltaSchaffner,您无法同时按顺序和并行执行操作。虽然元素可以由任意调用线程提供,但它们必须使用同步机制。短语“*对一个元素执行操作发生在对后续元素执行操作之前*”意味着您*可以*使用它来填充“ArrayList”等数据结构。因此,虽然不推荐,但它确实有效。使用“ThreadLocal”或类似的东西是行不通的...... (3认同)
  • @FedericoPeraltaSchaffner 好吧,我尝试过,它有效。Javadoc 说“对一个元素执行操作发生在对后续元素执行操作之前”,因此在本问题描述的场景中,Stream 的当前元素将在任何后续元素被添加到输出列表之前添加到输出列表中。添加。 (2认同)