In Java 8 one can set a custom forkJoinPool to be used by parallel streams rather than the common pool.
forkJoinPool.submit(() -> list.parallelStream().forEach(x ->{...} ))
My question is how does it technically happen?
The stream is not in any way aware it was submitted to a custom forkJoinpool and has no direct access to it. So how are the correct threads eventually used for processing the stream's tasks?
I tried looking at the source code but to no avail. My best guess is some threadLocal variable set at some point when submitting and then used by the stream later on. If so, why would the language developers choose such a way to implement the behaviour rather than, say, dependency injecting the pool into the stream?
Thanks!
The java.util.stream.ForEachOps.ForEachOp#evaluateParallel
method calls 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; }
which in turn calls 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(); }
As seen in the above method, it finds out the current thread using Thread.currentThread()
.
It then uses the .pool
field as in (wt = (ForkJoinWorkerThread)t).pool
, which gives the current pool that this thread is running in:
public class ForkJoinWorkerThread extends Thread { final ForkJoinPool pool; // the pool this thread works in
From what I've read the code, the decisions is made only based on the initial thread that triggers the computation, inside the method ForkJoinTask::fork
, that literally does a check against what thread triggered this (also in it's documentation):
Thread.currentThread()) instanceof ForkJoinWorkerThread
So if an instance of ForkJoinWorkerThread
has started this (this is what you would get via a custom ForkJoinPool
), use whatever the pool already exists and this task run in; otherwise (if it is a different thread that is not an instance of ForkJoinWorkerThread
) use:
ForkJoinPool.common.externalPush(this);
Also interesting that ForkJoinWorkerThread
is actually a public
class, so you could start the computation inside an instance of it, but still using a different pool; though I have not tried this.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.