java - 为什么我应该在带有收集的并行流中使用并发特性?

标签 java multithreading concurrency java-8 java-stream

为什么我应该在带有收集的并行流中使用并发特性:

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

换句话说,不使用这个特性有什么副作用,对内部流操作有用吗?

最佳答案

这两个收集器以根本不同的方式运作。

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

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

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


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

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

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

关于java - 为什么我应该在带有收集的并行流中使用并发特性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41041698/

相关文章:

java - 如何在 REST Assured 中设置请求超时?

java - Spring 安全 : how to intercept PageNotFound

java - 如何传入图形g

c# - 托管线程是否可能与其自身存在竞争条件

java - 从输入文本文件填充二维数组

python - 终止剩余的线程

java - java中如何让当前线程等待函数返回?

java - 在 for 循环中重用 Thread 对象

java - Spring数据redis并发问题

concurrency - 基础知识 |线程与响应式(Reactive)并发模型