我试图通过1Z0-809 Oracle认证,我只是在编写一些转储文件,然后我终于到了。
public final class ParallelStreams{
private long d=0,add=0,after=0;
public static void main(String[] args){
final ParallelStreams clazz = new ParallelStreams();
IntStream.iterate(1,p->p+1)
.peek(a->clazz.add++)
.limit(5)
.peek(a->clazz.after++)
.parallel()
.forEach(i->++clazz.d);
System.out.println("clazz = " + clazz.d+" "+clazz.add+" "+clazz.after);
}
}
我知道这不是最好的选择。但是确实引起了我的注意。的输出类似于
clazz = 5 15451 5
假设1次偷看方法被称为惊奇15451次,即使限制仅为5次?在所有线程中,当流有5个线程时,如何通过这条代码15451次来最终停止它,这应该怎么看?大概吗?
很抱歉,问题很简单,但对我来说真的很重要。
答案 0 :(得分:2)
当我执行您的程序时,我得到了输出:
clazz = 5 20 5
添加一些打印语句:
IntStream.iterate(1,p->p+1)
.peek(a->{ System.out.println("add - " + a); clazz.add++; })
.limit(5)
.peek(a->{ System.out.println("after - " + a); clazz.after++; })
.parallel()
.forEach(i->{ System.out.println("d - " + i); ++clazz.d; });
输出为:
add - 1025
add - 3073
add - 1
add - 6145
add - 2
add - 3074
add - 1026
add - 3075
add - 3
add - 6146
add - 6147
add - 4
add - 3076
add - 1027
add - 3077
add - 5
add - 6148
add - 6149
add - 1028
add - 1029
after - 3
after - 2
after - 5
d - 2
d - 3
d - 5
after - 1
after - 4
d - 1
d - 4
clazz = 5 20 5
要理解这一点,这里是文档摘录。
中间操作返回一个新的流。他们总是很懒惰。执行诸如filter()之类的中间操作实际上并不执行任何过滤,而是创建一个新的流,该新流在遍历时将包含与给定谓词匹配的初始流的元素。在执行管道的终端操作之前,不会开始遍历管道源。
对于并行流管道,可以在任何时间和线程中通过上游操作使元素可用,以调用该操作。
虽然limit()通常在顺序流管道上是便宜的操作,但在有序并行管道上却可能非常昂贵,尤其是对于maxSize大的值,因为limit(n)不仅要返回任何n个元素,而且还必须返回它遇到顺序中的前n个元素。
peek
不了解limit
操作,因此它在执行limit
之前被随机执行了N次。请注意,一旦打印出带有after -
和d -
的行,就不会打印出带有add -
的行。
这些after -
和d -
行从1到5打印,但是peek
和forEach
都不保证在使用并行流时流的顺序。
答案 1 :(得分:2)
您正在并行流上调用limit
。 limit
取决于遇到的顺序,并且在并行执行时,它必须缓冲所有先前的项目以确定是否将特定项目推到下一步。因此,它会在做出决定之前缓冲更多项目。您的方法本质上是顺序的,并且您尝试并行执行它,这是非常低效的,不会给您带来任何提速。解决此问题的一种方法是像这样从流管道中删除.parallel()
,
IntStream.iterate(1,p->p+1)
.peek(a->clazz.add++)
.limit(5)
.peek(a->clazz.after++)
.forEach(i->++clazz.d);
现在您将获得正确的结果。另一种解决方案是按以下方式删除IntStream.iterate
施加的遇到顺序。
IntStream.range(0, Integer.MAX_VALUE)
.peek(a->clazz.add++)
.limit(5)
.peek(a->clazz.after++)
.parallel()
.forEach(i->++clazz.d);
此解决方案可以使您并行运行。