The Java Fork/Join Framework is a powerful mechanism introduced in Java 7 (java.util.concurrent) for parallelizing divide-and-conquer algorithms within a multi-core processor environment. It’s particularly useful for tasks that can be broken down recursively into smaller tasks until they are small enough to be solved directly.
Important Components:
ForkJoinPool: Manages a pool of worker threads for executing Fork/Join tasks.
ForkJoinTask: Represents a task that can be forked (split) and joined (merged) with other tasks.
RecursiveTask: A subclass of ForkJoinTask for tasks that return a result.
RecursiveAction: A subclass of ForkJoinTask for tasks that do not return a result (perform actions).
Workflow:
Fork: The task divides itself into subtasks. This is typically done using the fork() method.
Join: The results of subtasks are combined (joined) to produce a final result. This is done using the join() method.
Base Case: Recursive tasks reach a base case where they are small enough to be computed sequentially.
Class Name | Description | Common Usage |
---|---|---|
ForkJoinPool |
A special thread pool designed for Fork/Join tasks. | Used to execute tasks created from RecursiveTask/Action |
RecursiveTask<V> |
Abstract class for tasks that return a result. | Extend it and override compute() to return a value. |
RecursiveAction |
Abstract class for tasks that don’t return a result. | Extend it and override compute() with void return. |
ForkJoinTask<V> |
The base class for both RecursiveTask and RecursiveAction. | Represents a task that can be run within ForkJoinPool. |
Important Methods of RecursiveTask
Method | Syntax | Description |
---|---|---|
compute() |
protected V compute() |
Abstract method to define the task logic (override this). |
fork() |
task.fork(); |
Asynchronously executes the task in another thread from the pool. |
join() |
V result = task.join(); |
Waits for completion and returns the result. |
invoke() |
V result = task.invoke(); |
Directly runs the task and returns the result (used inside compute too). |
invokeAll() |
invokeAll(task1, task2); |
Runs multiple tasks in parallel and waits for them. |
isDone() |
task.isDone(); |
Checks if task is completed (normal, exception, or cancelled). |
isCompletedNormally() |
task.isCompletedNormally(); |
True if task finished without exception or cancellation. |
cancel(boolean) |
task.cancel(true); |
Attempts to cancel the task. |
isCancelled() |
task.isCancelled(); |
Checks if the task was cancelled. |
Important Methods of ForkJoinPool
Method | Syntax | Description |
---|---|---|
Constructor | ForkJoinPool pool = new ForkJoinPool(); |
Creates a pool using the number of available processors. |
Constructor | ForkJoinPool pool = new ForkJoinPool(int parallelism); |
Creates a pool with a specific number of threads. |
invoke() |
V result = pool.invoke(task); |
Executes the task and waits for the result. |
submit() |
pool.submit(task); |
Submits a task to the pool for asynchronous execution. |
execute() |
pool.execute(task); |
Executes the given task but doesn’t return a result. |
shutdown() |
pool.shutdown(); |
Initiates an orderly shutdown of the pool. |
shutdownNow() |
pool.shutdownNow(); |
Attempts to stop all actively executing tasks immediately. |
awaitTermination() |
pool.awaitTermination(timeout, unit); |
Waits for termination within a given time. |
isShutdown() |
pool.isShutdown(); |
Checks if the pool has been shut down. |
isTerminated() |
pool.isTerminated(); |
Checks if all tasks are completed after shutdown. |
Â
Program
This program demonstrates how to use the Fork/Join Framework in Java to calculate the sum of an integer array using a divide-and-conquer strategy via the RecursiveTask
class.
//Calculating Sum using RecursiveTask //ForkJoinDemo.java import java.util.concurrent.RecursiveTask; import java.util.concurrent.ForkJoinPool; public class ForkJoinDemo { // RecursiveTask implementation to compute the sum of an array static class SumTask extends RecursiveTask<Integer> { private static final int THRESHOLD = 3; // Threshold to switch to sequential processing private int[] numbers; private int startIndex; private int endIndex; SumTask(int[] numbers, int startIndex, int endIndex) { this.numbers = numbers; this.startIndex = startIndex; this.endIndex = endIndex; } @Override protected Integer compute() { // If the range is smaller than the threshold, compute directly if (endIndex - startIndex <= THRESHOLD) { int sum = 0; for (int i = startIndex; i < endIndex; i++) { sum += numbers[i]; } return sum; } else { // Divide the task into smaller subtasks int mid = (startIndex + endIndex) / 2; SumTask leftTask = new SumTask(numbers, startIndex, mid); SumTask rightTask = new SumTask(numbers, mid, endIndex); // Fork the left task and compute the right task asynchronously leftTask.fork(); int rightResult = rightTask.compute(); // Join the results of the subtasks int leftResult = leftTask.join(); // Combine the results return leftResult + rightResult; } } } public static void main(String[] args) { // Create an array of integers int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // Create a ForkJoinPool with parallelism equal to the number of available processors ForkJoinPool forkJoinPool = new ForkJoinPool(); // Create a RecursiveTask to compute the sum of the array SumTask sumTask = new SumTask(numbers, 0, numbers.length); // Invoke the task in the ForkJoinPool int result = forkJoinPool.invoke(sumTask); // Print the result System.out.println("Sum= " + result); } } /* C:\>javac ForkJoinDemo.java C:\>java ForkJoinDemo Sum= 55 */
The Fork/Join Framework in Java provides an elegant way to parallelize tasks that can be divided recursively into smaller subtasks. It utilizes a work-stealing algorithm to efficiently distribute tasks across multiple processors, making it suitable for compute-intensive operations on multi-core systems.