为什么我应该在并行流中使用并发特性与收集? [英] Why should I use concurrent characteristic in parallel stream with collect?

查看:15
本文介绍了为什么我应该在并行流中使用并发特性与收集?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Why should I use concurrent characteristic in parallel stream with collect:

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));

也就是说,不使用这个特性有什么副作用,对内部流操作有用吗?

In other words, what are the side effects to not using this characteristic, Is it useful for the internal stream operations?

推荐答案

这两个收集器的操作方式完全不同.

These two collectors operate in a fundamentally different way.

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

First, the Stream framework will split the workload into independent chunks that can be processed in parallel (that’s why you don’t need a special collection as the source, synchronizedList is unnecessary).

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

With a non-concurrent collector, each chunk will be processed by creating a local container (here, a Map) using the Collector’s supplier and accumulating it into the local container (putting entries). These partial results have to be merged, i.e. one map has been put into the other, to get a final result.

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

A concurrent collector supports accumulating concurrently, so only one ConcurrentMap will be created and all threads accumulate into that map at the same time. So after completion, no merging step is required, as there is only one map.

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

So both collectors are thread-safe, but might exhibit entirely different performance characteristics, depending on the task. If the Stream’s workload before collecting the result is heavy, the differences might be negligible. If like in your example, there is no relevant work before the collect operation, the outcome heavily depends on how often mappings have to be merged, i.e the same key occurs, and how the actual target ConcurrentMap deals with contention in the concurrent case.

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

If you mostly have distinct keys, the merging step of a non-concurrent collector can be as expensive as the previous putting, destroying any benefit of the parallel processing. But if you have lots of duplicate keys, requiring merging of the values, the contention on the same key may degrade the concurrent collector’s performance.

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

So there’s no simple "which is better" answer (well, if there was such an answer, why bother adding the other variant). It depends on your actual operation. You can use the expected scenario as a starting point for selecting one but should measure with the real-life data then. Since both are equivalent, you can change your choice at any time.

这篇关于为什么我应该在并行流中使用并发特性与收集?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆