Core Java

Java 7: Fork/Join Framework Example

The Fork/Join Framework in Java 7 is designed for work that can be broken down into smaller tasks and the results of those tasks combined to produce the final result. In general, classes that use the Fork/Join Framework follow the following simple algorithm:
 
 
 
 
 
 
 

// pseudocode
Result solve(Problem problem) {
  if (problem.size < SEQUENTIAL_THRESHOLD)
    return solveSequentially(problem);
  else {
    Result left, right;
    INVOKE-IN-PARALLEL {
      left = solve(extractLeftHalf(problem));
      right = solve(extractRightHalf(problem));
    }
    return combine(left, right);
  }
}

In order to demonstrate this, I have created an example to find the maximum number from a large array using fork/join:

import java.util.Random;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;

public class MaximumFinder extends RecursiveTask<Integer> {

  private static final int SEQUENTIAL_THRESHOLD = 5;

  private final int[] data;
  private final int start;
  private final int end;

  public MaximumFinder(int[] data, int start, int end) {
    this.data = data;
    this.start = start;
    this.end = end;
  }

  public MaximumFinder(int[] data) {
    this(data, 0, data.length);
  }

  @Override
  protected Integer compute() {
    final int length = end - start;
    if (length < SEQUENTIAL_THRESHOLD) {
      return computeDirectly();
    }
    final int split = length / 2;
    final MaximumFinder left = new MaximumFinder(data, start, start + split);
    left.fork();
    final MaximumFinder right = new MaximumFinder(data, start + split, end);
    return Math.max(right.compute(), left.join());
  }

  private Integer computeDirectly() {
    System.out.println(Thread.currentThread() + ' computing: ' + start
                       + ' to ' + end);
    int max = Integer.MIN_VALUE;
    for (int i = start; i < end; i++) {
      if (data[i] > max) {
        max = data[i];
      }
    }
    return max;
  }

  public static void main(String[] args) {
    // create a random data set
    final int[] data = new int[1000];
    final Random random = new Random();
    for (int i = 0; i < data.length; i++) {
      data[i] = random.nextInt(100);
    }

    // submit the task to the pool
    final ForkJoinPool pool = new ForkJoinPool(4);
    final MaximumFinder finder = new MaximumFinder(data);
    System.out.println(pool.invoke(finder));
  }
}

The MaximumFinder class is a RecursiveTask which is responsible for finding the maximum number from an array. If the size of the array is less than a threshold (5) then find the maximum directly, by iterating over the array. Otherwise, split the array into two halves, recurse on each half and wait for them to complete (join). Once we have the result of each half, we can find the maximum of the two and return it.
 

Reference: Java 7: Fork/Join Framework Example from our JCG partner Fahd Shariff at the fahd.blog blog.

Fahd Shariff

Fahd is a software engineer working in the financial services industry. He is passionate about technology and specializes in Java application development in distributed environments.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

4 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Manoj
Manoj
9 years ago

In the compute api, instead of calling compute() api directly on right, we can fork it like below: @Override protected Integer compute() { final int length = end – start; if ( length < SEQUENTIAL_THRESHOLD ) { return computeDirectly(); } final int split = length / 2; final MaximumFinder left = new MaximumFinder( data, start, start + split ); left.fork(); final MaximumFinder right = new MaximumFinder( data, start + split, end ); // return max( right.compute(), left.join() ); // either we can return by calling right.compute(), else call right.fork, and then call max // on right.join and left.join right.fork(); return… Read more »

anonymous
anonymous
7 years ago

thanks for your post but i observed following things main purpose of fork/join is to improve performance by running task in parallel, but what i observed that time taken by sequential processing is always better than fork join processing. //code MaximumFinder finderSeq=new MaximumFinder(data); long startTimeSeq=System.nanoTime(); Integer maxValueSeq=finderSeq.computeDirectly(); long endTimeSeq=System.nanoTime(); System.out.println(“Seq Time For execution “+(endTimeSeq-startTimeSeq)+” max value “+maxValueSeq); // submit the task to the pool final ForkJoinPool pool = new ForkJoinPool(4); final MaximumFinder finder = new MaximumFinder(data); long startTimeParallel=System.nanoTime(); Integer maxValueParallel=pool.invoke(finder); long endTimeParallel=System.nanoTime(); System.out.println(“Parallel Time For execution “+(endTimeParallel-startTimeParallel)+” maxValueParallel “+maxValueParallel); //—————– Seq Time For execution 5037392 max value 99 Parallel Time… Read more »

eduardo milpas
eduardo milpas
6 years ago
Reply to  anonymous

For the guy that mentions the time is the same in sequence and parallel processing:

I’d like to see you implementation, I’ve run many times this example vs a sequential one and my results are:

ARRAY SIZE:1,000,000
max value [99] obtained in 196 millis
seq max value [99] obtained in 6 millis

eduardo milpas
eduardo milpas
6 years ago
Reply to  anonymous

My guess is that takes more time in parallel due to creation of new instances of MaximumFinder

Back to top button