Transcript Fork-Join

ForkJoin
New in Java 7
1Apr 5, 2012
Incrementor I
 package helloWorld;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
The first thing you need to do is to import some classes.
These are built into Java 7, but are also available as a separate download for
Java 6.
2
Incrementor II
 package helloWorld;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
class Incrementor extends RecursiveTask<Integer> {
Next you need to create a class that extends RecursiveTask.
A RecursiveTask is like a Thread, but you can retrieve a value from it after
it finishes. Supply a type parameter (Integer, in this example) to specify the
kind of value you want.
}
3
Incrementor III
 package helloWorld;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
class Incrementor extends RecursiveTask<Integer> {
public static ForkJoinPool fjPool = new ForkJoinPool();
Create a ForkJoinPool. Create only one of these, and it should be static.
A ForkJoinPool is a pool of Threads. Java will take care of all the thread
allocation and deallocation for you.
}
4
Incrementor IV
 package helloWorld;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
class Incrementor extends RecursiveTask<Integer> {
public static ForkJoinPool fjPool = new ForkJoinPool();
int theNumber;
Incrementor(int x) {
theNumber = x;
}
public Integer compute() {
return theNumber + 1;
}
Make an instance of our Incrementor class.
We defined it to have a “generic” type parameter
<Integer>, so let's define a field
“theNumber” to hold it.
In this example, our constructor sets theNumber
to have an initial value.
public static void main(String[] args) {
int fortyThree = fjPool.invoke(new Incrementor(42));
System.out.println(fortyThree);
}
}
5
Incrementor V
 package helloWorld;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
class Incrementor extends RecursiveTask<Integer> {
public static ForkJoinPool fjPool = new ForkJoinPool();
int theNumber;
Incrementor(int x) {
theNumber = x;
}
public Integer compute() {
return theNumber + 1;
}
Define a compute() method.
compute() is what we use in place of run().
public static void main(String[] args) {
int fortyThree = fjPool.invoke(new Incrementor(42));
System.out.println(fortyThree);
}
}
6
Incrementor VI
 package helloWorld;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
class Incrementor extends RecursiveTask<Integer> {
public static ForkJoinPool fjPool = new ForkJoinPool();
int theNumber;
Incrementor(int x) {
theNumber = x;
}
public Integer compute() {
return theNumber + 1;
}
Finally, create a new Incrementor (like getting a
Thread, but with a parameter), and tell the
ForkJoinPool to invoke it (like calling the
“start” method of a Thread).
Calling invoke will cause compute() to be
executed.
public static void main(String[] args) {
int fortyThree = fjPool.invoke(new Incrementor(42));
System.out.println(fortyThree);
}
}
7
What did that code do?
int fortyThree = fjPool.invoke(new Incrementor(42));





How is this any different from
int fortyThree = new Incrementor(42).compute();
?
Answer: compute runs as a RecursiveTask
(because class Incrementor extends RecursiveTask<Integer>)
So what?
RecursiveTasks are “lightweight” Threads (you can have up to 32767 of them)
which can fork and join


public final ForkJoinTask<V> fork() arranges to asynchronously execute this
task. Any given task should only be forked once.
public final V join(), unlike the Thread join, returns a result.
Summing an array I: Context
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
We only ever need or want one ForkJoinPool:
class Globals {
static ForkJoinPool fjPool = new ForkJoinPool();
}
Let’s make a RecursiveTask/compute() combination:
class Sum extends RecursiveTask<Long> {
protected Long compute() {…}
static long sumArray(int[] array) {…}
}
9
Summing an array II: Parameters
class Sum extends RecursiveTask<Long> {
static final int SEQUENTIAL_THRESHOLD = 5000;
int low;
int high;
int[] array;
Sum(int[]
array
low
high
}
arr, int lo, int hi) {
= arr;
= lo;
= hi;
We need to pass parameters in to
compute().
But compute() doesn’t take parameters!
What do we do?
Notice the constructor !
protected Long compute() {…}
static long sumArray(int[] array) {…}
}
10
Summing an array III: Doing the work
class Sum extends RecursiveTask<Long> {
protected Long compute() {
Not much to do? Do it here.
if(high - low <= SEQUENTIAL_THRESHOLD) {
long sum = 0;
for(int i=low; i < high; ++i)
sum += array[i];
return sum;
} else {
Split the array into two parts.
int mid = low + (high - low) / 2;
Sum left = new Sum(array, low, mid);
Sum right = new Sum(array, mid, high);
left.fork(); Start another task to sum the left half.
long rightAns = right.compute(); I’ll do the right half myself.
long leftAns = left.join(); When I’m done, wait for the other thread.
return leftAns + rightAns;
}
}
}
11
Let’s see that again
• We create two new Sums, one to fork and one to use ourself
• Sum left = new Sum(array, low, mid);
Sum right = new Sum(array, mid, high);
• We fork to sum the left half of the array
• left.fork();
• We sum the right half ourselves, with a plain old method call (not an
additional Thread) that returns a result
• long rightAns = right.compute();
• When we are done, the fork may or may not be done.
We wait for it. When it is done, we get its result
• long leftAns = left.join();
• Then, of course, we add the two results
• return leftAns + rightAns;
12
Summing an array IV: Begin here
•
class Sum extends RecursiveTask<Long> {
// other methods that we have been looking at
static long sumArray(int[] array) {
return Globals.fjPool.invoke(newSum(array,0,array.length));
}
}
The call to fjPool.invoke creates an instance of this Sum class, which is a RecursiveTask,
and that gets the ball rolling.
invoke should not be called from within a RecursiveTask or RecursiveAction.
It should only be called from sequential code.
15
Running the example
import java.util.Random;
public class SumTester {
static Random rand = new Random();
public static void main(String[] args) {
int[] array = new int[100000];
for (int i = 0; i < array.length; i += 1) {
array[i] = rand.nextInt(11);
}
long sum = Sum.sumArray(array);
System.out.println(sum);
}
}
14
The End
15