具有限制+并行的Java8 IntStream

时间:2018-06-27 00:22:16

标签: java-8

我试图通过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次来最终停止它,这应该怎么看?大概吗?

很抱歉,问题很简单,但对我来说真的很重要。

2 个答案:

答案 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

要理解这一点,这里是文档摘录。

java.util.stream

  

中间操作返回一个新的流。他们总是很懒惰。执行诸如filter()之类的中间操作实际上并不执行任何过滤,而是创建一个新的流,该新流在遍历时将包含与给定谓词匹配的初始流的元素。在执行管道的终端操作之前,不会开始遍历管道源。

IntStream.peek

  

对于并行流管道,可以在任何时间和线程中通过上游操作使元素可用,以调用该操作。

IntStream.limit

  

虽然limit()通常在顺序流管道上是便宜的操作,但在有序并行管道上却可能非常昂贵,尤其是对于maxSize大的值,因为limit(n)不仅要返回任何n个元素,而且还必须返回它遇到顺序中的前n个元素。

peek不了解limit操作,因此它在执行limit之前被随机执行了N次。请注意,一旦打印出带有after -d -的行,就不会打印出带有add -的行。

这些after -d -行从1到5打印,但是peekforEach都不保证在使用并行流时流的顺序。

答案 1 :(得分:2)

您正在并行流上调用limitlimit取决于遇到的顺序,并且在并行执行时,它必须缓冲所有先前的项目以确定是否将特定项目推到下一步。因此,它会在做出决定之前缓冲更多项目。您的方法本质上是顺序的,并且您尝试并行执行它,这是非常低效的,不会给您带来任何提速。解决此问题的一种方法是像这样从流管道中删除.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);

此解决方案可以使您并行运行。