Java 8的forEachOrdered()和sequential()方法之间的区别?

时间:2017-12-14 07:38:40

标签: java parallel-processing java-8 java-stream

我正在研究java 8并行流并且想要在并行流中打印元素是一些顺序(比如插入顺序,逆序或顺序)。

我尝试了以下代码:

class OopsExceptionHandler : ExceptionHandler
{
    public override void HandleCore(ExceptionHandlerContext context)
    {
        context.Result = new TextPlainErrorResult//you have to create this class
        {
            Request = context.ExceptionContext.Request,
            Content = "Oops! Sorry! Something went wrong." +
                      "Please contact support@contoso.com so we can try to fix it."
        };
    }
}

对于这两个,我得到了相同的输出如下:

        System.out.println("With forEachOrdered:");
        listOfIntegers
            .parallelStream()
            .forEachOrdered(e -> System.out.print(e + " "));
        System.out.println("");

        System.out.println("With Sequential:");
        listOfIntegers.parallelStream()
                    .sequential()
                    .forEach(e -> System.out.print(e + " "));

从api文档中,我可以看到:

  

forEachOrdered - >这是终端操作。

  

顺序 - >这是一个中间操作。

所以我的问题是哪一个更好用? 在哪种情况下,一个应该优先于其他?

3 个答案:

答案 0 :(得分:4)

listOfIntegers.parallelStream().sequential().forEach()创建了一个并行Stream,然后将其转换为顺序Stream,因此您也可以改为使用listOfIntegers.stream().forEach(),并获得顺序Stream首先。

listOfIntegers.parallelStream().forEachOrdered(e -> System.out.print(e + " "))对并行Stream执行操作,但保证元素将按Stream的遭遇顺序使用(如果Stream有定义的遭遇订购)。但是,它可以在多个线程上执行。

我没有看到使用listOfIntegers.parallelStream().sequential()的原因。如果您想要顺序Stream,为什么要首先创建并行Stream

答案 1 :(得分:3)

你问的是一个误导性的问题,首先你要问:

 .parallelStream()
 .forEachOrdered(...)

这将创建并行流,但元素将按顺序消耗。如果您添加map这样的操作:

.map(...)
.parallelStream()
.forEachOrdered(...)

这将使map非常有限(从并行处理的角度来看)操作,因为线程必须等待处理遭遇顺序中的所有其他元素(由forEachOrdered消耗)。这涉及无国籍行动。

另一方面,如果你有有状态操作,如:

.parallelStream()
.map()
.sorted()
.// other operations

由于sorted是有状态的,因此并行处理之前之前的无状态操作的好处将更大。之所以发生这种情况是因为sorted必须从Stream中收集所有元素,并且线程不必“等待”(在forEachOrdered)处理遇到顺序的元素。

对于第二个例子:

listOfIntegers.parallelStream()
                .sequential()
                .forEach(e -> System.out.print(e + " "))

你基本上是说并行打开然后关闭它。流是由终端操作驱动的,所以即使你这样做:

 .map...
 .filter...
 .parallel()
 .map...
 .sequential

这意味着整个管道将按顺序执行,而不是某些部分将是并行的而另一部分是顺序的。你也依赖于forEach保留秩序的事实,可能就在它发生的那一刻,但可能在以后的版本中,正如你说你不关心订单(使用forEach首先,将有一个内部洗牌的元素。

答案 2 :(得分:2)

流管道可以顺序执行,也可以并行执行。此执行模式是流的属性。通过初始选择的顺序或并行执行来创建流。例如,Collection.stream()创建一个顺序流,Collection.parallelStream()创建一个并行流。可以通过BaseStream.sequential()BaseStream.parallel()方法修改执行模式的选择。

所以没有必要使用:

listOfIntegers.parallelStream().sequential()

您只能使用:

listOfIntegers.stream()

如果要创建parallel stream,则可以通过不同的线程处理流的元素。 forEachforEachOrdered之间的区别在于forEach将允许以任何顺序处理并行流的任何元素,而forEachOrdered将始终按照它们的外观顺序处理并行流的元素。原始流。使用parallelStream()时,forEachOrdered是一个非常好的示例,说明如何利用多个内核并保持输出顺序。请注意,forEachOrdered以有序的方式强制迭代流的元素。但是,在forEachOrdered之前链接的任何操作仍然会并行发生,因为该流是并行流。

Oracle没有记录在管道中多次更改流执行模式时会发生什么。目前尚不清楚最后一次更改是否重要,或者调用parallel()后调用的操作是否可以并行执行,调用sequential()后调用的操作是否会按顺序执行。