并行流如何“知道”以使用封闭的ForkJoinPool?

时间:2019-07-18 07:24:20

标签: java java-stream forkjoinpool

在Java 8中,可以设置一个自定义forkJoinPool,以供并行流而不是公共池使用。

forkJoinPool.submit(() -> list.parallelStream().forEach(x ->{...} ))

我的问题是,它在技术上是如何发生的?
流不知道以任何方式将其提交到自定义forkJoinpool,并且无法直接访问它。那么最终如何使用正确的线程来处理流的任务?

我尝试查看源代码,但无济于事。我最好的猜测是在提交时在某个时候设置了一些threadLocal变量,然后稍后由流使用。如果是这样,为什么语言开发人员会选择这种方式来实现该行为,而不是将依赖项注入到流中呢?

谢谢!

2 个答案:

答案 0 :(得分:1)

java.util.stream.ForEachOps.ForEachOp#evaluateParallel方法调用invoke()

@Override
public <S> Void evaluateParallel(PipelineHelper<T> helper,
                                 Spliterator<S> spliterator) {
    if (ordered)
        new ForEachOrderedTask<>(helper, spliterator, this).invoke();
    else
        new ForEachTask<>(helper, spliterator, helper.wrapSink(this)).invoke();
    return null;
}

依次调用java.util.concurrent.ForkJoinTask#doInvoke

private int doInvoke() {
    int s; Thread t; ForkJoinWorkerThread wt;
    return (s = doExec()) < 0 ? s :
        ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
        (wt = (ForkJoinWorkerThread)t).pool.
        awaitJoin(wt.workQueue, this, 0L) :
        externalAwaitDone();
}

如上述方法所示,它使用Thread.currentThread()查找当前线程。

然后它像.pool一样使用(wt = (ForkJoinWorkerThread)t).pool字段,它提供了该线程在其中运行的当前池:

public class ForkJoinWorkerThread extends Thread {

    final ForkJoinPool pool;                // the pool this thread works in

答案 1 :(得分:1)

根据我读过的代码,该决定仅基于触发计算的初始线程(仅在方法ForkJoinTask::fork 做出),该方法实际上会检查线程触发了这一点(也在其文档中):

Thread.currentThread()) instanceof ForkJoinWorkerThread

因此,如果ForkJoinWorkerThread的实例已启动此操作(这是通过自定义ForkJoinPool获得的结果),请使用池中已存在的任何内容运行此任务;否则(如果它是另一个线程,而不是ForkJoinWorkerThread的实例),请使用:

ForkJoinPool.common.externalPush(this); 

有趣的是ForkJoinWorkerThread实际上是public类,因此您可以在其实例内部开始计算,但仍使用其他池;尽管我还没有尝试过。