简述
ForkJoin是由JDK1.7后提供多线并发处理框架。它的基本思想是分而治之。
分而治之
规模为N的问题,N<阈值,直接解决,N>阈值,将N分解为K个小规模子问题,子问题互相对立,与原问题形式相同,将子问题的解合并得到原问题的解。
ForkJoin的工作秘取(work-stealing)
由于ForkJoin的执行时多线程的,因此总会出现,有的线程已经执行完了,其他线程还没执行或者还在执行的情况,这个时候执行完的线程假如只是等待另一个线程执行完,效率就太低了。因此,在线程A执行完毕后,处理线程B的任务,并且执行完毕后将任务归还给B。这样就可以提高执行效率。而这种就是工作秘取。
使用
使用ForkJoin框架,需要创建一个ForkJoin的任务,ForkJoinTask是一个抽象类,并且它还有RecursiveAction和RecursiveTask两个子类,我们需要实现其中的一个实现compute方法。然后把task通过ForkJoinPool来执行。
RecursiveTask和RecursiveAction区别
RecursiveTask:需要返回值时使用
RecursiveAction: 没有返回值时使用
例子
public class ForkJoin {
private static class SumTask extends RecursiveTask<Long> {
private Integer[] sumArray; //需要计算的数组
private int start; //开始下标
private int end; //结束下标
private final long THRESHOLD = 1000; //阈值,大于该值时进行分而治之处理
SumTask(Integer[] sumArray, int start, int end) {
this.sumArray = sumArray;
this.start = start;
this.end = end;
}
protected Long compute() {
if (end - start < THRESHOLD) {
//计算数小与阈值时直接单线程计算返回
long count = 0;
for (int i = start; i <= end; i++) {
/*try {
Thread.sleep(1); //模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}*/
count += sumArray[i];
}
return count;
} else {
int mid = (start + end) / 2;
SumTask sumTask1 = new SumTask(sumArray, start, mid);
SumTask sumTask2 = new SumTask(sumArray, mid + 1, end);
invokeAll(sumTask1, sumTask2);
return sumTask1.join() + sumTask2.join();
}
}
}
public static void main(String[] args) {
//生成一个数组
Integer[] sumArray = new Integer[10000];
for (int i = 0; i < sumArray.length; i++) {
sumArray[i] = i;
}
//使用ForkJoin进行计算
ForkJoinPool forkJoinPool = new ForkJoinPool();
SumTask sumTask = new SumTask(sumArray, 0, sumArray.length - 1);
long startTime = System.currentTimeMillis();
forkJoinPool.invoke(sumTask);
long endTime = System.currentTimeMillis();
System.out.println("ForkJoin执行时间为:" + (endTime - startTime) + "ms,结果为:" + sumTask.join());
//直接单线程计算
long count = 0;
long startTime1 = System.currentTimeMillis();
for(int i = 0; i < sumArray.length; i++){
/*try {
Thread.sleep(1); //模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}*/
count += sumArray[i];
}
long endTime1 = System.currentTimeMillis();
System.out.println("单线程执行时间为:" + (endTime1 - startTime1) + "ms,结果为:" + count);
}
}
结果:
多线程不一定效率比单线程高
这里是一个面试点,在上面的例子中,可以很清楚的看出,使用单线程比使用多线程计算用了更少的时间。出现这种情况,是因为逻辑本身就非常的简单,不是耗时操作,而多线程需要进行上下文切换,会消耗额外的时间,因此在执行简单的逻辑或者非耗时操作时多线程反而效率比不上单线程。
可以把例子的注释打开,每一次计算时,将线程休眠一毫秒,就可以看到差距。