Fork/Join Framework

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.

Scroll to Top