CompletableFuture / ForkJoinPool Set Class Loader

I ran into something similar and came up with a solution that does not use reflection and seems to work well with JDK9-JDK11.

Here is what the javadocs say:

The parameters used to construct the common pool may be controlled by setting the following system properties:

  • java.util.concurrent.ForkJoinPool.common.threadFactory – the class name of a ForkJoinPool.ForkJoinWorkerThreadFactory. The system class loader is used to load this class.

So if you rollout your own version of the ForkJoinWorkerThreadFactory and set that instead to use the correct ClassLoader using the system property, this should work.

Here is my custom ForkJoinWorkerThreadFactory:

package foo;

public class MyForkJoinWorkerThreadFactory implements ForkJoinWorkerThreadFactory {

    @Override
    public final ForkJoinWorkerThread newThread(ForkJoinPool pool) {
        return new MyForkJoinWorkerThread(pool);
    }

    private static class MyForkJoinWorkerThread extends ForkJoinWorkerThread {

        private MyForkJoinWorkerThread(final ForkJoinPool pool) {
            super(pool);
            // set the correct classloader here
            setContextClassLoader(Thread.currentThread().getContextClassLoader());
        }
    }
} 

and then set the system property in your app startup script

-Djava.util.concurrent.ForkJoinPool.common.threadFactory=foo.MyForkJoinWorkerThreadFactory

The above solution works assuming that when the ForkJoinPool class is referenced the first time and it initializes the commonPool, the context ClassLoader for this Thread is the correct one that you need (and is not the System class loader).

Here is some background that might help:

Fork/Join common pool threads return the system class loader as their thread context class loader.

In Java SE 9, threads that are part of the fork/join common pool will always return the system class loader as their thread context class loader. In previous releases, the thread context class loader may have been inherited from whatever thread causes the creation of the fork/join common pool thread, e.g. by submitting a task. An application cannot reliably depend on when, or how, threads are created by the fork/join common pool, and as such cannot reliably depend on a custom defined class loader to be set as the thread context class loader.

As a result of the above backward incompatibility change, things that uses the ForkJoinPool that used to worked in JDK8 may not work in JDK9+ .

Leave a Comment