Why are fork join tasks executed outside the common fork join pool threads?

2.2k views Asked by At

My question can best be explained by giving a code snippet:

public static void main(final String[] a) {
    Stream.of(1, 2, 3, 4).map(i -> ForkJoinPool.commonPool().submit(new RecursiveAction() {
        @Override
        protected void compute() {
            System.out.println(Thread.currentThread());
        }
    })).forEach(ForkJoinTask::join);
}

When running this on my laptop, which has 4 cores, this prints:

Thread[main,5,main]
Thread[ForkJoinPool.commonPool-worker-1,5,main]
Thread[main,5,main]
Thread[ForkJoinPool.commonPool-worker-1,5,main]

Why are certain tasks ran in the main thread, which is a thread outside the common fork join thread pool?

When creating a custom fork join thread pool, this is not happening:

public static void main(final String[] a) {
    final ForkJoinPool p = new ForkJoinPool(4);

    Stream.of(1, 2, 3, 4).map(index -> p.submit(new RecursiveAction() {
        @Override
        protected void compute() {
            System.out.println(Thread.currentThread());
        }
    })).forEach(ForkJoinTask::join);
}

Thread[ForkJoinPool-1-worker-1,5,main]
Thread[ForkJoinPool-1-worker-1,5,main]
Thread[ForkJoinPool-1-worker-1,5,main]
Thread[ForkJoinPool-1-worker-1,5,main]

So, in other words, what is special about the common pool? Giving this knowledge, would it be a wise or unwise idea to execute long-running tasks in the common pool?

2

There are 2 answers

0
Stephen C On BEST ANSWER

Something rather clever happens.

When you call ForkJoinTask::join from a thread that is not in the thread, it appears (from the comment on ForkJoinPool.awaitJoin) that current thread can "helps" with task execution.

So that's why the main thread is executing tasks in the fork-join pool.

But why is it different in the case where you created a custom pool? Well, my guess is that your custom pool has enough threads that the main thread is not needed. Because the other thing is that by default the "common pool" is created with one thread fewer than the number of available processors.


For more details look at the source code. Note that this behavior is not specified in the javadocs, so it could change in future implementations.

1
edharned On

To expound on my other comments:

Using the submitting thread as a work thread has always been about performance. In a work-stealing-first program, the submitting thread puts the new request into a submission queue and notifies the work threads that there is work. (Exactly which thread(s) get(s) notified has changed over time.)

Work-stealing threads only get work by stealing from other thread’s deques or by going to the submission queue. (There are now multiple submission queues because of the scaling problem of only using one.) Exactly how a thread finds work is irrelevant. What is relevant is that it is slow. Threads need to wake up and go looking for work. There is no way to give work to any thread since the thread’s queues are deques. The main reason for this design is that the author copied the design from Cilk.

Cilk is cluster fork/join program. It mainly works in a cluster environment where the computers connect in a network. Having a work-sharing algorithm where the computers query the queues of other computers to see where there is work, or, where to put the forked task (into the queue with the least number of pending tasks for load balancing) is prohibitive. Therefore, in a cluster environment work-stealing-first with deques is preferable.

In Java, the environment is Shared Memory. There is minuscule overhead looking in the queues of other threads. In any case, the first request for the framework requires a wake-up, go-looking-for-work from the threads and it is slow.

For Java8 parallel streams the system needed the common pool and a way to speed up the process. (The initial timing for parallel threads was awful.) Therefore, the author came up with the idea to use the submitting thread as a worker. However, this technique introduced problems of its own as a mentioned here: http://coopsoft.com/ar/Calamity2Article.html#submission