繁体   English   中英

使用parallelstream()在Java 8中填充Map是否安全

[英]Is it safe to use parallelstream() to populate a Map in Java 8

我有一个包含100万个对象的列表,我需要将其填充到Map中。 现在,我想减少将其填充到Map中的时间,为此我计划使用Java 8 parallelstream(),如下所示:

List<Person> list = new LinkedList<>();
Map<String, String> map = new HashMap<>();
list.parallelStream().forEach(person ->{
    map.put(person.getName(), person.getAge());
});

我想问一下,通过并行线程填充这样的Map是否安全。 难道不可能出现并发问题,并且某些数据可能会在Map中丢失吗?

使用parallelStream()收集 HashMap是非常安全的。 但是,使用parallelStream()forEach和消费者向HashMap添加内容是不安全的。

HashMap不是同步类,并且尝试同时将元素放入其中将无法正常工作。 这就是forEach将要做的事情,它将调用给定的使用者,它可以同时从多个线程将元素放入HashMap 如果你想要一个简单的代码来证明这个问题:

List<Integer> list = IntStream.range(0, 10000).boxed().collect(Collectors.toList());
Map<Integer, Integer> map = new HashMap<>();
list.parallelStream().forEach(i -> {
    map.put(i, i);
});
System.out.println(list.size());
System.out.println(map.size());

一定要运行几次。 操作后打印的地图大小不是10000,这是列表的大小,但稍微少一点,这是一个非常好的机会(并发的乐趣)。

这里的解决方案一如既往不是使用forEach ,而是使用collect方法和内置toMap可变缩减方法:

Map<Integer, Integer> map = list.parallelStream().collect(Collectors.toMap(i -> i, i -> i));

使用在上面的示例代码行的代码,你可以放心,地图大小将始终是10000的流API确保它是安全的 ,收集到非线程安全的容器,即使是在平行。 这也意味着您不需要使用toConcurrentMap是安全的,如果您特别想要ConcurrentMap作为结果,而不是一般Map ,则需要此收集器; 但就线程安全而言,关于collect ,你可以使用两者。

HashMap不是线程安全的,但是ConcurrentHashMap是; 用它代替

Map<String, String> map = new ConcurrentHashMap<>();

并且您的代码将按预期工作。


forEach()toMap()性能比较

在JVM预热后,使用1M元素,使用并行流和使用中值时序, forEach()版本始终比toMap()版本快2-3倍。

结果在所有独特的,25%重复和100%重复输入之间是一致的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM