目录
6、complete / tryComplete / helpComplete / internalPropagateException
RecursiveAction / RecursiveTask / CountedCompleter 这3个类是ForkJoinTask对外的三个子类,因为ForkJoinTask本身不是直接使用的,所以先了解其子类的具体使用方式和实现细节,后面的博客再探讨ForkJoinTask和ForkJoinPool的实现。
一、RecursiveAction
1、普通的递归实现
RecursiveAction表示一个没有返回值的递归执行的任务,该类继承自ForkJoinTask,该类的定义如下:
其中我们能改写的只有compute方法,实现具体的计算逻辑,另外三个都是final方法,无法改写。 测试用例如下:
class IntAdder extends RecursiveAction {
private static final long serialVersionUID = 5349972192067990608L;
private final List<Integer> datas;
private final int threshold;
private long result;
public IntAdder(List<Integer> datas, int level) {
super();
this.datas = datas;
this.threshold = level;
}
public static int sum(List<Integer> list) {
int sum = 0;
for (Integer i : list) {
sum += i;
}
return sum;
}
@Override
protected void compute() {
System.out
.println(Thread.currentThread().getName() + " start to work,data size->"+datas.size());
if (datas.size() < threshold) {
result = sum(datas);
} else {
int toIndex = datas.size() / 2;
IntAdder left = new IntAdder(datas.subList(0,
toIndex), threshold);
IntAdder right = new IntAdder(datas.subList(
toIndex, datas.size()), threshold);
System.out.println(Thread.currentThread().getName() + " start wait,data size->"+datas.size());
//阻塞当前线程,等待两个子任务执行完成
//底层逻辑是立即执行left,将right提交到任务队列,left执行完成后,然后等待right执行完成
invokeAll(left,right);
System.out.println(Thread.currentThread().getName() + " end wait,data size->"+datas.size());
result = left.result+right.result;
}
}
public long getResult() {
return result;
}
private static List<Integer> createTestDatas(int count) {
List<Integer> list = new ArrayList<Integer>();
Random random = new Random();
for (int i = 0; i < count; i++) {
list.add(random.nextInt(10000));
}
return list;
}
/**
* @param args
*/
public static void main(String[] args) {
int count = 100000;
int level = count / 5;
List<Integer> datas = createTestDatas(count);
long before = System.currentTimeMillis();
long sum = 0;
for (Integer i : datas) {
sum += i;
}
System.out.println("result->" + sum + ",time->" + (System.currentTimeMillis() - before));
IntAdder t = new IntAdder(datas, level);
ForkJoinPool pool = new ForkJoinPool(Runtime.getRuntime()
.availableProcessors() * 4);
before = System.currentTimeMillis();
//会阻塞当前线程,直到任务执行完成
pool.invoke(t);
System.out.println("result->" + t.getResult() + ",time->"
+ (System.currentTimeMillis() - before));
}
}
上述测试用例的输出如下:
result->498313847,time->4
//worker-9是第一个执行任务的线程,先执行left,不断递归,直到可以不用切分出子任务,计算完left,等待right执行完成,然后逐级向上返回
ForkJoinPool-1-worker-9 start to work,data size->100000
ForkJoinPool-1-worker-9 start wait,data size->100000
ForkJoinPool-1-worker-9 start to work,data size->50000
ForkJoinPool-1-worker-9 start wait,data size->50000
ForkJoinPool-1-worker-9 start to work,data size->25000
ForkJoinPool-1-worker-9 start wait,data size->25000
ForkJoinPool-1-worker-9 start to work,data size->12500
ForkJoinPool-1-worker-9 end wait,data size->25000
ForkJoinPool-1-worker-9 end wait,data size->50000
ForkJoinPool-1-worker-9 end wait,data size->100000
//worker-2是第二个执行任务的线程,执行worker-9切分出来的right任务,同样的会不断递归,逻辑同上
ForkJoinPool-1-worker-2 start to work,data size->50000
ForkJoinPool-1-worker-2 start wait,data size->50000
ForkJoinPool-1-worker-2 start to work,data size->25000
ForkJoinPool-1-worker-2 start wait,data size->25000
ForkJoinPool-1-worker-2 start to work,data size->12500
ForkJoinPool-1-worker-2 start to work,data size->12500
ForkJoinPool-1-worker-2 end wait,data size->25000
ForkJoinPool-1-worker-2 end wait,data size->50000
//worker-11和worker-4是第三个和第四个线程
ForkJoinPool-1-worker-11 start to work,data size->25000
ForkJoinPool-1-worker-11 start wait,data size->25000
ForkJoinPool-1-worker-11 start to work,data size->12500
ForkJoinPool-1-worker-11 end wait,data size->25000
ForkJoinPool-1-worker-6 start to work,data size->12500
ForkJoinPool-1-worker-6 start to work,data size->12500
ForkJoinPool-1-worker-4 start to work,data size->25000
ForkJoinPool-1-worker-4 start wait,data size->25000
ForkJoinPool-1-worker-4 start to work,data size->12500
ForkJoinPool-1-worker-4 end wait,data size->25000
ForkJoinPool-1-worker-10 start to work,data size->12500
result->498313847,time->7
从耗时看,因为存在提交任务到线程的递归fork动作和让线程休眠等待的join操作,在任务量较的情形下使用ForkJoinPool的性能比单线程要差一半。
2、非递归实现
还有一种用法可以显著改进上述示例中的递归fork问题,如下:
package synchronizedTest;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
class IntAdder2 extends RecursiveAction {
private static final long serialVersionUID = 5349972192067990608L;
private final List<Integer> datas;
private final int threshold;
private long result;
private IntAdder2 next;
public IntAdder2(List<Integer> datas, int level, IntAdder2 next) {
super();
this.datas = datas;
this.threshold = level;
this.next=next;
}
public static int sum(List<Integer> list) {
int sum = 0;
for (Integer i : list) {
sum += i;
}
return sum;
}
@Override
protected void compute() {
System.out
.println(Thread.currentThread().getName() + " start to work,data size->"+datas.size());
int start = 0, size = datas.size();
IntAdder2 right = null;
while (size > threshold) {
right = new IntAdder2(datas.subList(start, start + threshold), threshold,right);
//创建一个子任务,提交到任务队列
right.fork();
start += threshold;
size -= threshold;
}
long sum = sum(datas.subList(start, datas.size()));
while (right!=null) {
if (right.tryUnfork()) { //尝试将该任务从任务队列中移除,由当前线程执行
sum += sum(right.datas);
} else {
right.join(); //该任务已经在执行了,则等待任务执行完成
sum += right.result;
}
right=right.next;
}
result=sum;
}
public long getResult() {
return result;
}
private static List<Integer> createTestDatas(int count) {
List<Integer> list = new ArrayList<Integer>();
Random random = new Random();
for (int i = 0; i < count; i++) {
list.add(random.nextInt(10000));
}
return list;
}
/**
* @param args
*/
public static void main(String[] args) {