为什么我要在并行流中使用并发特性和collect?

为什么我要在并行流中使用并发特性和collect:

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

并不是:

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

换句话说,不使用此特性会产生什么副作用,它对内部流操作有用吗?

这两个collections家以完全不同的方式运作。

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

对于非并发收集器,将使用收集器的供应商创建本地容器(此处为Map )并累积到本地容器(放置条目)来处理每个块。 必须合并这些部分结果,即将一个地图放入另一个地图,以获得最终结果。

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


因此,两个收集器都是线程安全的,但可能会表现出完全不同的性能特征,具体取决于任务。 如果Stream在收集结果之前的工作量很大,那么差异可能会微不足道。 如果像在您的示例中那样,在收集操作之前没有相关工作,则结果在很大程度上取决于映射必须合并的频率,即发生相同的密钥,以及实际目标ConcurrentMap如何处理并发情况下的争用。

如果您主要使用不同的密钥,则非并发收集器的合并步骤可能与之前的放置一样昂贵,从而破坏了并行处理的任何好处。 但是,如果您有许多重复的密钥,需要合并这些值,则对同一密钥的争用可能会降低并发收集器的性能。

所以没有简单的“哪个更好”的答案(好吧,如果有这样的答案,为什么还要加入另一个变体)。 这取决于您的实际操作。 您可以使用预期方案作为选择方案的起点,但应使用实际数据进行衡量。 由于两者都是等价的,您可以随时更改您的选择。

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

CONCURRENT – >多个线程以无特定顺序在同一个容器中抛出数据(ConcurrentHashMap)

非并发 – >多个线程组合其中间结果。

理解它(IMHO)的最简单方法是编写一个自定义收集器并使用它的每个方法:供应商,累加器,组合器。

这已经在这里涵盖了

正因为如此:“内存一致性影响:与其他并发集合一样,在将对象作为键或值放入ConcurrentMap之前的线程中的操作发生在从另一个中的ConcurrentMap访问或删除该对象之后的操作之前线。”