java8 stream运行原理之并行流原理详解

上一篇文章《java8 stream运行原理之顺序流原理详解》介绍了顺序流的执行原理,本文接着上一篇介绍并行流的执行原理。

一、如何创建并行流

调用parallel()方法可以创建并行流,如下:

    public static void main(String argv[]){
        Stream<String> stream=Stream.of("1","2","","123");
        stream.filter(x->x.length()>=1).parallel().forEach(System.out::println);
    }

二、并行流原理

下面以第一小节的代码为例,介绍一下并行流原理。
这里只介绍最后的终端操作(forEach),对于如何创建Stream流,以及中间操作原理请参见上一篇文章。

1、parallel()

首先来看一下parallel()方法:

    public final S parallel() {
    	//sourceStage是Head对象引用
    	//将Head对象的parallel属性设置为true
        sourceStage.parallel = true;
        return (S) this;
    }

parallel()方法仅仅将Head对象的parallel属性设置为true。

2、forEach()

下面是forEach()方法源码:

    public void forEach(Consumer<? super P_OUT> action) {
    	//makeRef()创建TerminalOp对象
        evaluate(ForEachOps.makeRef(action, false));
    }
    final <R> R evaluate(TerminalOp<E_OUT, R> terminalOp) {
        assert getOutputShape() == terminalOp.inputShape();
        if (linkedOrConsumed)
            throw new IllegalStateException(MSG_STREAM_LINKED);
        linkedOrConsumed = true;
		//检查sourceStage.parallel的值,如果为true,表示是并行流
        return isParallel()
               ? terminalOp.evaluateParallel(this, sourceSpliterator(terminalOp.getOpFlags()))
               : terminalOp.evaluateSequential(this, sourceSpliterator(terminalOp.getOpFlags()));
    }

在forEach()里面调用了terminalOp.evaluateParallel()进行并行处理。下面是terminalOp.evaluateParallel()的方法:

    public <S> Void evaluateParallel(PipelineHelper<T> helper,
                                     Spliterator<S> spliterator) {
        //ordered表示是否有序遍历,true表示有序
        //forEach操作默认ordered都是false
        if (ordered)
            new ForEachOrderedTask<>(helper, spliterator, this).invoke();
        else
            new ForEachTask<>(helper, spliterator, helper.wrapSink(this)).invoke();
        return null;
    }

在evaluateParallel()方法里面创建ForEachTask对象。
ForEachTask实现了ForkJoinTask类,而这个ForkJoinTask是Fork/Join框架中的,这是java7新增的功能。Fork/Join框架对并行处理做了很多优化。
上面代码的最后调用了invoker()方法,这个方法会调用到ForEachTask.compute()方法:

public void compute() {
	  //将数据源拆分为两部分,分别交给两个不同的线程处理,
	  //这两部分使用rightSplit和leftSplit记录
      Spliterator<S> rightSplit = spliterator, leftSplit;
      long sizeEstimate = rightSplit.estimateSize(), sizeThreshold;
      if ((sizeThreshold = targetSize) == 0L)
          targetSize = sizeThreshold = AbstractTask.suggestTargetSize(sizeEstimate);
      boolean isShortCircuit = StreamOpFlag.SHORT_CIRCUIT.isKnown(helper.getStreamAndOpFlags());
      boolean forkRight = false;
      Sink<S> taskSink = sink;
      ForEachTask<S, T> task = this;
      while (!isShortCircuit || !taskSink.cancellationRequested()) {
          //rightSplit.trySplit()可以对数据源的数据进行拆分,将数据一分为二
          //如果剩余的数据量不足以进行再次拆分,则直接使用当前线程处理
          if (sizeEstimate <= sizeThreshold ||
              (leftSplit = rightSplit.trySplit()) == null) {
              task.helper.copyInto(taskSink, rightSplit);
              break;
          }
          //将数据拆分为两部分后,左半部分的数据再创建ForEachTask对象
          ForEachTask<S, T> leftTask = new ForEachTask<>(task, leftSplit);
          task.addToPendingCount(1);
          ForEachTask<S, T> taskToFork;
          //forkRight相当于一个开关,如果上次启动任务处理左半部分数据,那么这次启动任务处理右半部分数据
          if (forkRight) {
              forkRight = false;
              rightSplit = leftSplit;
              taskToFork = task;
              task = leftTask;
          }
          else {
              forkRight = true;
              taskToFork = leftTask;
          }
          //调用fork()可以将任务加入到待处理队列中,后续线程池中的线程会将任务取走处理
          taskToFork.fork();
          sizeEstimate = rightSplit.estimateSize();
      }
      task.spliterator = null;
      task.propagateCompletion();
  }
}		

并行流处理的核心逻辑就在compute()方法里面,下面总结一下并行流的执行流程:

  1. 调用Spliterator.trySplit()拆分流,将流一拆为二,如果不能再拆分,那么调用Sink对象链表处理数据;
  2. 拆分为两部分数据后,右半部分的数据使用当前ForEachTask对象处理,左半部分数据新创建一个ForEachTask对象处理,之后分别启动线程处理这两个任务。

如果处理流的操作都是无状态的,那么执行会像上面介绍的一样在终端操作里面创建并行任务,如果中间操作有有状态的,那么在创建Spliterator对象时,会先将该有状态操作以及之前的所有操作并行执行一次,得到的结果作为Spliterator对象,然后将该Spliterator对象传递给终端操作,这一段处理逻辑可以参见方法sourceSpliterator():

    private Spliterator<?> sourceSpliterator(int terminalFlags) {
        // Get the source spliterator of the pipeline
        Spliterator<?> spliterator = null;
        if (sourceStage.sourceSpliterator != null) {
            spliterator = sourceStage.sourceSpliterator;
            sourceStage.sourceSpliterator = null;
        }
        else if (sourceStage.sourceSupplier != null) {
            spliterator = (Spliterator<?>) sourceStage.sourceSupplier.get();
            sourceStage.sourceSupplier = null;
        }
        else {
            throw new IllegalStateException(MSG_CONSUMED);
        }
		//如果是并行流且有有状态的操作,那么下面的if分支判断为true
        if (isParallel() && sourceStage.sourceAnyStateful) {
            // Adapt the source spliterator, evaluating each stateful op
            // in the pipeline up to and including this pipeline stage.
            // The depth and flags of each pipeline stage are adjusted accordingly.
            int depth = 1;
            //从Head开始遍历各个操作对象
            for (@SuppressWarnings("rawtypes") AbstractPipeline u = sourceStage, p = sourceStage.nextStage, e = this;
                 u != e;
                 u = p, p = p.nextStage) {

                int thisOpFlags = p.sourceOrOpFlags;
                //如果当前操作是有状态的
                if (p.opIsStateful()) {
                    depth = 0;//操作对象链表深度记为0
                    if (StreamOpFlag.SHORT_CIRCUIT.isKnown(thisOpFlags)) {
                        // Clear the short circuit flag for next pipeline stage
                        // This stage encapsulates short-circuiting, the next
                        // stage may not have any short-circuit operations, and
                        // if so spliterator.forEachRemaining should be used
                        // for traversal
                        thisOpFlags = thisOpFlags & ~StreamOpFlag.IS_SHORT_CIRCUIT;
                    }
					//将数据进行并行处理,只执行当前有状态的操作以及它前面的操作
					//将操作的结果创建一个Spliterator对象,
					//该Spliterator对象接下来就作为后面流处理的数据源
                    spliterator = p.opEvaluateParallelLazy(u, spliterator);

                    // Inject or clear SIZED on the source pipeline stage
                    // based on the stage's spliterator
                    thisOpFlags = spliterator.hasCharacteristics(Spliterator.SIZED)
                            ? (thisOpFlags & ~StreamOpFlag.NOT_SIZED) | StreamOpFlag.IS_SIZED
                            : (thisOpFlags & ~StreamOpFlag.IS_SIZED) | StreamOpFlag.NOT_SIZED;
                }
                //操作对象链表深度加1 ,如果中间操作有有状态的,那么该有状态的操作深度为0,
                //相当于接下来以该有状态操作对象作为链表起点
                p.depth = depth++;
                p.combinedFlags = StreamOpFlag.combineOpFlags(thisOpFlags, u.combinedFlags);
            }
        }
        if (terminalFlags != 0)  {
            // Apply flags from the terminal operation to last pipeline stage
            combinedFlags = StreamOpFlag.combineOpFlags(terminalFlags, combinedFlags);
        }
        return spliterator;
    }
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值