The 30 second answer
ExecutorService is the general interface for running tasks on a pool; the classic version, ThreadPoolExecutor, has workers that take tasks from one shared queue. ForkJoinPool is one implementation built for work stealing: each worker has its own queue, and idle workers steal tasks from busy ones. It fits divide-and-conquer and many small tasks. Parallel streams and default CompletableFuture async calls use its common pool.
ExecutorService io = Executors.newFixedThreadPool(32); // ThreadPoolExecutor
ExecutorService vt = Executors.newVirtualThreadPerTaskExecutor(); // Java 21
class Sum extends RecursiveTask<Long> {
final long[] a; final int lo, hi;
Sum(long[] a, int lo, int hi) { this.a = a; this.lo = lo; this.hi = hi; }
protected Long compute() {
if (hi - lo < 10_000) { long s = 0; for (int i = lo; i < hi; i++) s += a[i]; return s; }
int mid = (lo + hi) >>> 1;
Sum left = new Sum(a, lo, mid); left.fork();
return new Sum(a, mid, hi).compute() + left.join();
}
}
long total = ForkJoinPool.commonPool().invoke(new Sum(arr, 0, arr.length));ThreadPoolExecutor: core/max pool size, a work queue and a rejection policy. Good for independent, coarse tasks, including blocking ones.ForkJoinPool: per-worker deques; a worker processes its own tasks and steals from others when idle. Good for recursive splitting. It also implementsExecutorService.- The common pool's default parallelism is one less than the number of available processors. Blocking inside it slows every parallel stream in the JVM. [Opinion] Use separate executors for blocking I/O.
Sources: ForkJoinPool Javadoc, ExecutorService Javadoc, ThreadPoolExecutor Javadoc
Sources (0)
See inline citations in the answer above.