我有一个这样的测试代码:
List<Integer> list = new ArrayList<>(1000000);
for(int i=0;i<1000000;i++){
list.add(i);
}
List<String> values = new ArrayList<>(1000000);
list.stream().forEach(
i->values.add(new Date().toString())
);
System.out.println(values.size());
运行这个,我得到了正确的输出:1000000。
但是,如果我改变stream()
to parallelStream()
,如下所示:
list.parallelStream().forEach(
i->values.add(new Date().toString())
);
我得到一个随机输出,例如:920821。
怎么了?
An ArrayList不同步。尝试同时向其中添加元素未定义。从forEach:
对于并行流管道,此操作不能保证尊重流的相遇顺序,因为这样做会牺牲并行性的好处。对于任何给定的元素,可以在库选择的任何时间和任何线程中执行该操作.
在你的第二个例子中,你最终会得到多个线程调用add
同时在数组列表上并且ArrayList
文档说:
请注意,此实现不是同步的。如果多个线程同时访问ArrayList实例,并且至少有一个线程在结构上修改了列表,则必须进行外部同步。
错误的解决方案
如果您改变使用ArrayList
to a Vector,您将得到正确的结果,因为此列表实现是同步的。它的 Javadoc 说:
与新的集合实现不同,Vector
已同步。
然而,不要使用它!此外,由于显式同步,它最终可能会变慢。
正确的做法
Stream API 明确地避免了这种情况,提供了可变还原范例,使用collect方法。下列
List<String> values = list.stream().map(i -> "foo").collect(Collectors.toList());
无论是否并行运行,都将始终提供正确的结果。 Stream 管道在内部处理并发性和保证在并行流的收集操作中使用非并发收集器是安全的. Collectors.toList()是一个内置收集器,将 Stream 的元素累积到列表中。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)