【问题标题】:Parallel stream finishes before the task is done并行流在任务完成之前完成
【发布时间】:2018-01-19 13:28:19
【问题描述】:

我用这个方法用默认值初始化一个ArrayList:

private ArrayList<Integer> createArrayList(int size, int defaultValue)
{
    ArrayList<Integer> arrayList = new ArrayList<>(size);
    IntStream.range(0, size ).parallel().forEach(i->{arrayList.add(defaultValue);});

    return arrayList;
}

令人惊讶的是,如果我用单元测试检查返回数组的大小,它不是固定数字,而且几乎总是小于请求的大小。

如果我删除parallel(),它的大小总是符合要求的。

为什么会这样?

【问题讨论】:

  • 因为 arraylist 不是线程安全的集合,而您正试图在其中同时插入元素。

标签: java parallel-processing thread-safety java-stream


【解决方案1】:

当然它更小,您将多个线程中的多个元素添加到非线程安全的ArrayList(当元素无法填充其原始大小时,它会重新调整大小)。

你可以这样做更简单:

Collections.nCopies(i, defaultValue);

【讨论】:

  • 而变小只是无效的多线程ArrayList 修改的一种可能结果。另一种可能性是结果列表中的虚假 null 元素。 nCopies 是当不可变列表是可接受的结果时要走的路。如果 OP 真的需要 ArrayList,可以像 new ArrayList&lt;&gt;(Collections.nCopies(i, defaultValue)) 这样简单地创建它...
【解决方案2】:

使用线程安全的 CopyOnWriteArrayList 而不是 ArrayList

private ArrayList<Integer> createArrayList(int size, int defaultValue)
{
    List<Integer> list = new CopyOnWriteArrayList<>(size);
    IntStream.range(0, size ).parallel().forEach(i->{list.add(defaultValue);});

    return list;
}

【讨论】:

  • side-effects... 你正在打破这个,即使你以“更安全”的方式这样做
  • 是的,完全意识到这一点.. 只是给 OP 一些立即可用的东西..
  • 如果你是,为什么不给他一个合适的map(...).collect(Collectors.toCollection(() -&gt; new ArrayList(size)))
  • 可能有一些解决方案效率更低,但你必须非常努力地找到它们......
猜你喜欢
  • 1970-01-01
  • 2016-07-16
  • 1970-01-01
  • 2017-08-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-15
相关资源
最近更新 更多