为什么我应该在和集合中使用并行特征



为什么我应该在平行流中使用并发特征与收集:

List<Integer> list =
        Collections.synchronizedList(new ArrayList<>(Arrays.asList(1, 2, 4)));
Map<Integer, Integer> collect = list.stream().parallel()
        .collect(Collectors.toConcurrentMap(k -> k, v -> v, (c, c2) -> c + c2));

而不是:

Map<Integer, Integer> collect = list.stream().parallel()
        .collect(Collectors.toMap(k -> k, v -> v, (c, c2) -> c + c2));

换句话说,不使用此特征的副作用是什么?

有用

这两个收藏家以根本不同的方式运行。

首先,流框架将将工作负载分为可以并行处理的独立块(这就是为什么您不需要特殊集合作为源,synchronizedList是不必要的)。

使用非电流收集器,每个块将通过使用收集器的供应商创建本地容器(此处,Map)来处理每个块,并将其堆积到本地容器(放入条目)中。这些部分结果必须合并,即一个地图已被放入另一个地图,以获得最终结果。

并发收集器同时支持积累,因此将仅创建一个ConcurrentMap,并且所有线程同时累积到该地图中。因此,完成后,不需要合并步骤,因为只有一张地图。


因此,两个收集器都是线程安全的,但是根据任务,可能表现出完全不同的性能特征。如果在收集结果之前流的工作量很重,则差异可能可以忽略不计。如果像您的示例中,在收集操作之前没有相关的工作,则结果在很大程度上取决于必须合并映射的频率,即同一键发生,以及实际目标ConcurrentMap在并发情况下如何处理争议。<<<<<<<<<<<<<<<<<<<<<<<<<</p>

如果您大多有独特的键,则非电流收集器的合并步骤可能与以前的推杆一样昂贵,从而破坏了并行处理的任何好处。但是,如果您有很多重复的键,需要合并值,那么同一密钥上的争论可能会降低并发收集者的性能。

因此,没有简单的"更好"答案(好吧,如果有这样的答案,为什么要添加其他变体)。这取决于您的实际操作。您可以将预期的方案用作选择一个方案的起点,但应使用现实生活数据进行测量。由于两者都是等效的,因此您可以随时更改自己的选择。

首先,我给霍尔格的答案给了 1,这是一个很好的答案。我会尝试简单地说:

并发 ->多个线程以相同容器以没有特定顺序(conurrenthashmap)

非电流 ->多个线程结合了它们的中间结果。

理解它的最简单方法(恕我直言)是编写一个自定义收藏家并使用它的每种方法播放:供应商,累加器,组合。

这已经在这里涵盖了

因此:"内存一致性效果:与其他并发集合一样,将对象放入contrentMap中的键或值之前,在访问或从另一线程中的contrentMap中删除该对象的操作之前,将对象作为键或值进行。" <<<<<<<<<<<<<<<<

最新更新