Open In App

Difference Between Fork/Join Framework and ExecutorService in Java

Last Updated : 18 Jul, 2022
Improve
Improve
Like Article
Like
Save
Share
Report

The Fork/Join framework provides fine-grained task execution framework with high performance for Java data parallelism. Its parallel computing engine is used by many higher-level frameworks. The fork/join framework supports a style of parallel programming that solves problems by “Divide and conquer”, in the following manner as shown below:

  1. Splitting a task into sub-tasks.
  2. Solving sub-tasks in parallel
    • Sub-tasks can run in parallel on different cores.
    • Sub-tasks can also run concurrently in different threads on a single core.
  3. Waiting for them to complete
    • join() waits for a sub-task to finish
  4. Merging the results.
    • A task uses calls to join() to merge the sub-task results together.

Java Fork-Join Pool Computation Model

If a task does not return a result then it just waits for its sub-tasks to complete.

Below is a Java program to demonstrate the working of Fork/Join Framework :

Java




// Java program to demonstrate the working of Fork/Join
// Framework
 
// Importing required libraries
import java.io.*;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
 
// Class 1
// helper class
class SearchTask extends RecursiveTask<Integer> {
 
    // Global variables
    int array[];
    int start, end;
    int searchElement;
 
    // Constructor for initialising globals
    public SearchTask(int array[], int start, int end,
                      int searchElement)
    {
 
        // This keyword refers to current object itself
        this.array = array;
        this.start = start;
        this.end = end;
        this.searchElement = searchElement;
    }
 
    // Method
    // @Override
    protected Integer compute()
    {
 
        // Returns the count computed by processSearch
        return processSearch();
    }
 
    // Method
    // To count the count of searched element
    private Integer processSearch()
    {
 
        // Initially count is set to zero
        int count = 0;
 
        // iterating using for loop
        for (int i = start; i <= end; i++) {
 
            // if element is present in array
            if (array[i] == searchElement) {
 
                // Increment the count
                count++;
            }
        }
 
        // Returning the count of searched element
        return count;
    }
}
 
// Class 2
//  Main class
public class GFG {
 
    // main driver method
    public static void main(String args[])
    {
 
        // Custom input array elements
        int array[] = { 1, 2, 6, 3456,
                        7, 8, 9, 10, 11, 12, 6 };
 
        // Custom element to be searched in array
        int searchElement = 6;
 
        // initializing starting and ending indices
        int start = 0;
        int end = array.length - 1;
 
        // Creating object of ForkJoinPool class
        ForkJoinPool pool = ForkJoinPool.commonPool();
 
        // Now creating object of above class
        SearchTask task = new SearchTask(array, start, end,
                                         searchElement);
        int result = pool.invoke(task);
 
        // Print and display the searched element
        // If found do display out the number of times it is
        // found
        System.out.println(searchElement + " found "
                           + result + " times ");
    }
}


Output

6 found 3 times 

Now dwelling onto The Java ExecutorService interface extends Executor so we get the one and only execute(Runnable) method defined by Executor. There are a lot more methods in Java ExecutorService compared to Java Executor. Some of the methods in the ExecutorService interface can be used to submit one or more tasks and returns something called a future(essentially a proxy to the result of a computation that runs concurrently and or asynchronously in the background).

The ExecutorService works in the following manner as follows: 

  1. Submit 1+ tasks and return futures for these tasks.
  2. Manage the lifecycle of tasks and executor service itself, e.g., interrupts worker threads in a pool.
  3. An ExecutorService instance can be in one of three states
    • Running: After being created via a factory method.
    • Shutting Down: After being shut down gracefully or abruptly.
    • Terminated: After all, tasks have completed.

Implementation:

Example 

Java




// Java program to demonstrate the working of
// ExecutorService
 
// Importing required libraries
import java.io.*;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
 
// Class 1
// helper class extending Runnable interface
class Service implements Runnable {
 
    // member variable of this class
    int i;
 
    // Constructor of this class
    public Service(int i)
    {
 
        // Initializing the counter variable
        this.i = i;
    }
 
    // Method
    // @Override
    public void run()
    {
 
        // Printing the counter
        System.out.println(i + " ");
 
        // Try block to check for exceptions
        try {
 
            // Making thread to sleep for 1 second
            // using the sleep() method
            Thread.sleep(1000);
        }
 
        // Catch block to handle the exceptions
        catch (InterruptedException e) {
 
            // Print the line number and the corresponding
            // exception occurred
            e.printStackTrace();
        }
    }
}
 
// Class 2
// Main class
// ExecutorUtility
public class GFG {
 
    // Main driver method
    public static void main(String[] args)
    {
 
        // Creating an object of ExecutorService class to
        // create fixed size thread pool
        ExecutorService es
            = Executors.newFixedThreadPool(5);
 
        // Print the time difference before completion
        System.out.println(new Date());
 
        for (int i = 0; i < 25; i++) {
 
            // Executes the given command at some time in
            // the future
            es.execute(new Service(i));
        }
 
        // Executor is shut down so that
        // its task can be considered complete
        es.shutdown();
 
        // Print the time difference after completion
        System.out.println(new Date());
    }
}


Output: 

Now finally let us conclude the differences between Fork/Join Framework and ExecutorService which ais as follows: 

Fork/Join Framework  ExecutorService
The Fork/Join framework in Java 7 is an implementation of the Divide and Conquer algorithm, in which a central ForkJoinPool executes branching ForkJoinTasks. ExecutorService is an Executor that provides methods to manage the progress-tracking and termination of asynchronous tasks.
Fork/Join Framework makes use of Work Stealing Algorithm. In the Fork/Join framework, when a task is waiting for the completion of the sub-tasks it has created using the join operation, the worker thread that is executing that task looks for another task that has not been executed yet and steals them to start their execution. Unlike Fork/Join Framework, when a task is waiting for the completion of the sub-tasks it has created using the join operation,  the worker thread that is executing that waiting task doesn’t look for another task.                                                                                                           
Fork-join is wonderful for recursive problems, where a task involves running subtasks and then processing their results.  If you try to solve a recursive problem like this using ExecutorService, you end up with threads tied up waiting for other threads to deliver results to them.
Fork Join is an implementation of ExecuterService. The main difference is that this implementation creates a DEQUE worker pool. Executor service creates asked number of thread, and apply a blocking queue to store all the remaining waiting task.

 



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads