The Fork Join Framework in Java7
The Fork Join Framework in Java7
com Page 1 of 6
BY
Kishori Sharan
Code examples in this post are from Chapter 7. Threads of the book Harnessing Java 7 (Volume 2)
----------------------------------------------------------------------------------------------------------------------------- -----
The fork/join framework creates a pool of threads to execute the subtasks. When a thread is waiting
on a subtask to finish, the fork/join framework uses that thread to execute other pending subtasks
of other threads. The technique of an idle thread executing other thread’s task is called work
stealing. The fork/join framework uses a work-stealing algorithm to enhance the performance.
The following four classes are central to learning the fork/join framework. All of them are in the
java.util.concurrent package.
ForkJoinPool
ForkJoinTask
RecursiveAction
RecursiveTask
types of tasks: a task that does not yield a result and a task that yields a result. An instance of the
RecursiveAction class represents a task that does not yield a result. An instance of the
RecursiveTask class represents a task that yields a result.
if (Task is small) {
Solve the task directly.
}
else {
Divide the task into subtsaks.
Launch the subtasks asynchronously (the fork stage).
Wait for the subtasks to finish (the join stage).
Combine the results of all subtasks.
}
The following two methods of the ForkJoinTask class provide two important features during a
task execution.
The fork() method launches a new subtask from a task for an asynchronous execution.
The join() method lets a task wait for another task to complete.
The logic to execute your task goes inside the compute() method of your class. The return type of
the compute() method is the same as the type of the result that your task returns. The declaration
for the compute() method of the MyTask class would look as shown below.
You can create a pool of worker threads to execute your task using the ForkJoinPool class. The
default constructor of this class creates a thread of pool, which has the same parallelism as the
number of processors available on the machine.
Other constructors let you specify the parallelism and other properties of the pool.
You need to call the invoke() method of the ForkJoinPool class passing your task as an
argument. The invoke() method will return the result of the task, if your task returns a result. The
following statement will execute our task.
Let us consider a simple example of using the fork/join framework. We will generate a few random
integers and compute their sum. Listing 1 has the complete code for our task. The class is named
RandomIntSum. It extends RecursiveTask<Long>, because it yields a result of Long type. The
result is the sum of all random integers. It declares a randGenerator instance variable, which is
used to generate a random number. The count instance variable stores the number of random
numbers that we want to use. The value for the count instance variable is set in the constructor.
The compute() method contains the main logic to perform the task. If the number of random
numbers to use is 1, it computes the result and returns it to the caller. If the number of random
number is more than one, it launches as many subtasks as the number of random numbers. Note
that if we use ten random numbers, it will launch ten subtasks, because each random number can
be computed independently. We will need to combine the results from all subtasks. Therefore, we
need to keep the references of the subtask for later use. We use a List to store the references of
all subtasks. Note the use of the fork() method to launch a subtask. The following snippet of
code performs this logic.
Once all subtasks are launched, we need to wait on all subtasks to finish and combine all random
numbers to get the sum. The following snippet of code performs this logic. Note the use of the
join() method, which will make the current task wait on the subtask to finish.
Finally, the compute() method returns the result, which is the sum of all the random numbers.
Listing 2 has the code to execute a task, which is an instance of the RandomIntSum class.
This is a very simple example of using the fork/join framework. You are advised to explore the
fork/join framework classes to know more about the framework. Inside the compute() method of
your task, you can have complex logic to divide tasks into subtasks. Unlike in our example, you
may not know in advance how many subtasks you need to launch. You may launch a subtask,
which may launch another subtask and so on.
// RandomIntSum.java
package com.jdojo.chapter7;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.RecursiveTask;
@Override
protected Long compute() {
long result = 0;
if (this.count <= 0) {
return 0L; /* We do not have anything to do */
}
if (this.count == 1) {
/* Compute the number directly and return the result */
return (long) this.getRandomInteger();
}
/* Now wait for all subtasks to finish and combine the result */
for(RecursiveTask<Long> subTask : forks) {
result = result + subTask.join();
}
return result;
}
return n;
}
}
// ForkJoinTest.java
package com.jdojo.chapter7;
import java.util.concurrent.ForkJoinPool;