library
advancedFork/Join Framework
Divide computational tasks into subtasks recursively and process them in parallel with work-stealing.
Fork/join splits a task into subtasks, runs them in parallel, and combines the results.
Fork/Join = a team of chefs preparing a banquet. The head chef (main task) divides the menu (fork). Each chef works on their dishes. If one finishes early, they help others (work stealing). Finally, all dishes are combined (join).
Key Concepts
1
class SumTask extends RecursiveTask<Long> {
private final long[] a; private final int lo, hi;2
protected Long compute() {
if (hi - lo <= 10_000) { // base case: just do it
long s = 0;
for (int i = lo; i < hi; i++) s += a[i];
return s;
}
int mid = (lo + hi) >>> 1;
SumTask left = new SumTask(a, lo, mid);
left.fork(); // queue it
long right = new SumTask(a, mid, hi).compute(); // do this one here
return right + left.join(); // wait for the other
}
}3
long total = ForkJoinPool.commonPool().invoke(new SumTask(data, 0, data.length));4
The idiom in that code is worth copying. Fork one half, compute the other on the current thread, then
join. Forking both halves and joining both wastes a thread doing nothing.
5
The two task types.
RecursiveTask<V> returns a result
RecursiveAction returns nothing6
Work stealing is what makes it scale. Every worker has its own double-ended queue. It pushes and pops
its own tasks at one end; an idle worker steals from the other end. There is no central queue to contend on,
and load balances itself.
7
The threshold decides whether it pays.
too small more time spent creating tasks than working
too large idle cores
start at size / availableProcessors, then measure8
Parallel streams run on this. list.parallelStream() builds fork/join tasks over a Spliterator, using
ForkJoinPool.commonPool() by default.
list.parallelStream()SpliteratorForkJoinPool.commonPool()
9
Which is the gotcha. The common pool is shared by the whole JVM and sized to cores minus one. One
blocking task in it starves everything else, including other parallel streams:
10
new ForkJoinPool(4).submit(() -> list.parallelStream()...).get();11
Run your own pool when the work might block, and keep blocking out of the common pool entirely.
12
And it is for CPU-bound divide-and-conquer. For blocking I/O, virtual threads are the better tool — they
unmount instead of occupying a worker.
13
What the interviewer is probing.1. "What makes fork/join different from a normal thread pool?" Probing: work stealing.
Stalls: "It is just a pool." Moves up: each worker has its own deque and idle workers steal from
the other end, so there is no central queue to contend on and load balances itself.
14
2. "Why fork one half and compute the other directly?" Probing: the idiom. Stalls: "Fork
both, it is more parallel." Moves up: forking both leaves the current thread waiting; computing
one keeps it busy and halves the task overhead.
15
3. "What goes wrong if you block inside the common pool?" Probing: the shared resource.
Stalls: "Nothing." Moves up: the common pool is sized to cores minus one and shared by every
parallel stream in the JVM, so one blocking task starves everything else.
16
4. "How do you pick the threshold?" Probing: whether you measure or guess. Stalls: "A round
number." Moves up: start around size divided by available processors and benchmark — too small and
task creation dominates, too large and cores idle.