Java > Concurrency and Multithreading > Executors and Thread Pools > ForkJoinPool
ForkJoinPool Example: Recursive Task for Summing Array Elements
This example demonstrates how to use the ForkJoinPool to recursively sum the elements of a large array. It illustrates the divide-and-conquer strategy, where the array is split into smaller sub-arrays until a manageable size is reached, then the sub-sums are calculated and combined.
Code Snippet: RecursiveTask Implementation
This code defines a `SumArray` class that extends `RecursiveTask
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
class SumArray extends RecursiveTask<Long> {
private final long[] array;
private final int start;
private final int end;
private final int threshold = 10000; // Size at which to compute directly
public SumArray(long[] array, int start, int end) {
this.array = array;
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
int length = end - start;
if (length <= threshold) {
long sum = 0;
for (int i = start; i < end; i++) {
sum += array[i];
}
return sum;
} else {
int middle = start + length / 2;
SumArray left = new SumArray(array, start, middle);
SumArray right = new SumArray(array, middle, end);
left.fork(); // Asynchronously compute the left half
long rightResult = right.compute(); // Compute the right half directly
long leftResult = left.join(); // Wait for the left half to complete
return leftResult + rightResult;
}
}
public static void main(String[] args) {
long[] numbers = new long[1000000];
for (int i = 0; i < numbers.length; i++) {
numbers[i] = i + 1; // Initialize with some values
}
ForkJoinPool pool = new ForkJoinPool();
SumArray task = new SumArray(numbers, 0, numbers.length);
long sum = pool.invoke(task);
System.out.println("Sum of array elements: " + sum);
pool.shutdown();
}
}
Concepts Behind the Snippet
This snippet demonstrates the core principles of the ForkJoin framework: Divide and Conquer: The problem (summing the array) is broken down into smaller, independent sub-problems. Recursive Decomposition: The sub-problems are solved recursively. Work-Stealing: Idle threads in the pool steal work from busy threads, improving overall efficiency and load balancing. RecursiveTask: The `RecursiveTask` is an abstract class that represents a recursive, result-bearing fork/join computation.
Real-Life Use Case
Image processing is a real-life example of the ForkJoin framework. You can apply filters, perform transformations, or analyze regions of an image concurrently by dividing the image into smaller blocks and processing each block in parallel using `ForkJoinPool`.
Best Practices
Interview Tip
Be prepared to discuss the work-stealing algorithm used by `ForkJoinPool`. Explain how idle threads steal tasks from the queues of busy threads to improve overall efficiency. Also, understand the difference between `fork()` and `join()` methods and their impact on concurrency.
When to Use ForkJoinPool
Use `ForkJoinPool` when you have a computationally intensive task that can be broken down into smaller, independent subtasks. It is particularly well-suited for recursive algorithms and problems that exhibit a natural divide-and-conquer structure.
Memory Footprint
The memory footprint of `ForkJoinPool` can be significant, especially when dealing with a large number of tasks. Each task requires its own memory for its execution context and data. Carefully consider the size and number of tasks to avoid excessive memory consumption.
Alternatives
Alternatives to `ForkJoinPool` include:
Pros
Cons
FAQ
-
What is the purpose of the `threshold` variable?
The `threshold` variable determines the size at which a sub-task is considered small enough to be computed directly, rather than being further subdivided. It helps to avoid excessive overhead from task creation. -
What does the `fork()` method do?
The `fork()` method asynchronously executes the task. It places the task in the work queue of the current thread or another thread in the pool, allowing it to be processed concurrently. -
What does the `join()` method do?
The `join()` method waits for the task to complete its execution and returns the result. It blocks the current thread until the task is finished. -
Why is it important to shutdown the ForkJoinPool?
Shutting down the `ForkJoinPool` releases the resources held by the pool, preventing resource leaks and ensuring proper termination of the application. Threads in the pool will be terminated when the pool is shutdown.