尝试使用流我遇到了以下不太明白的行为。我从迭代器创建了一个并行流,我注意到它似乎没有表现出并行性。在下面的例子中,我为控制台打印了一个计数器,用于两个并行流,一个是从迭代器创建的,另一个是从列表中创建的。从列表创建的流展示了我期望的行为,即以非顺序顺序打印计数器,但是从迭代器创建的流按顺序打印计数器。我是否错误地从迭代器创建并行流?
private static int counter = 0;
public static void main(String[] args) {
List<Integer> lstr = IntStream.rangeClosed(1, 100).boxed().collect(Collectors.toList());
Iterator<Integer> iter = lstr.iterator();
System.out.println("Iterator Stream: ");
StreamSupport.stream(Spliterators.spliteratorUnknownSize(iter, Spliterator.IMMUTABLE | Spliterator.CONCURRENT), true).forEach(i -> {
System.out.print(counter + " ");
counter++;
});
counter = 0;
System.out.println("\nList Stream: ");
lstr.parallelStream().forEach(i -> {
System.out.print(counter + " ");
counter++;
});
}
答案 0 :(得分:6)
当前的实现将尝试通过缓冲值并将它们分派给多个线程来并行化从迭代器生成的流,但只有在流足够长时它才会启动。将列表增加到10000个元素,您应该看到并行性。
使用大型列表,如果您收集到按线程分组的地图中,可能更容易看到线程分配的元素。将.forEach
替换为.collect(Collectors.groupingBy(x -> Thread.currentThread().getName(), Collectors.counting()))
答案 1 :(得分:6)
并不保证并行处理以非连续顺序打印计数器。此外,由于您在没有同步的情况下更新变量,因此可能会错过其他线程所做的更新,因此结果可能完全不一致。
除此之外,必须按顺序轮询Iterator
,因此要从并行处理中获得至少一些增益,必须缓冲元素,但是没有已知大小,对于有多少元素没有很好的估计缓冲。默认策略使用了超过一千个元素,并且不能很好地分割工作。
因此,如果您使用超过数千个元素,您可能会注意到更多并行活动。或者,您可以使用StreamSupport.stream(Spliterators.spliterator(iter, lstr.size(), 0), true)
指定大小来构造流。然后,将调整内部使用的缓冲。
然而,List
的流将具有更高效的并行处理,因为它不仅知道其大小,还支持利用底层数据结构的随机访问性质来分割工作负载。