概述
Fork/Join框架:Java1.7中提供的并行执行任务框架,具体模式为:将主任务逐级拆分为多级子任务并行执行,最终通过结果合并得到主任务的执行结果
图示
ForkJoinTask
ForkJoinTask:基本任务类,创建任务类必须继承基本任务类的子类,重写compute方法;提供fork方法,join方法与get方法
主要子类:
RecursiveTask: 用于有返回结果的任务 RecursiveAction: 用于无返回结果的任务 CountedCompleter: 用于无返回结果的任务 特殊之处在于任务执行完compute方法后就结束执行 并不会等待子任务执行完成 同级子任务均执行完成后可触发结果合并 并将合并结果提交给上级任务
fork方法:
将子任务放入当前工作线程的双端队列 volatile WorkQueue[] workQueues;
join方法:
阻塞等待至成功获取任务的执行结果 通常用于向上级任务提交任务的执行结果(若任务有子任务 则阻塞等待至所有子任务都执行完成 此时获取的执行结果为所有子任务执行结果的合并结果)
get方法:
与join方法大同小异 区别在于get方法可以抛出ExecutionException与InterruptedException异常 通常用于获取最终的执行结果(主任务的执行结果 task.get() task为任务类实例或submit方法的返回值)
ForkJoinPool
ForkJoinPool :ExecutorService 的实现类,用于执行 ForkJoinTask(亦可以接收Runnable/Callable 任务,只是在运行时均会被包装为 ForkJoinTask )
线程池中的一个工作线程(ForkJoinWorkerThread)不能同时执行多个任务
线程池不会为每个子任务创建单独的工作线程,Fork/Join框架将根据当前正在并发执行的工作线程的状态决定:将子任务放入当前线程的双端队列,或创建一个新的工作线程执行子任务,或唤醒正在等待任务的工作线程执行子任务
Fork/Join框架使用工作窃取机制( Work-Stealing )平衡线程的工作负载
工作窃取( Work-Stealing )
工作窃取:闲置的线程尝试从忙碌的线程的双端队列中窃取工作
通常情况下,工作线程从自身的双端队列中获取任务,但当自身的双端队列为空时,就会尝试从其他忙碌的线程的双端队列尾部或全局入口处获取任务,以此提高整体的执行效率
执行方法
方法 | 特点 |
---|---|
invoke() | 阻塞至任务及其所有子任务执行完成才返回,即同步执行,返回值为task.join() |
submit() | 立刻返回,即异步执行,返回值为task |
execute() | 立刻返回,即异步执行,无返回值 |
注:task为主任务
示例:以 RecursiveTask 类为例
任务类:
public class ForkJoinDemo extends RecursiveTask<Long> {
long start;
long end;
//临界值
long temp = 10000L;
public ForkJoinDemo(long start, long end) {
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
if ((end-start)<temp){
long sum = 0L;
for (long i = start; i < end; i++) {
sum += i;
}
return sum;
} else {
long middle = (start + end)/2;
//将当前task拆分 创建子任务(类似于递归 若拆分后end-start仍然大于temp 会在新建的task内部继续拆分)
ForkJoinDemo task01 = new ForkJoinDemo(start, middle);
ForkJoinDemo task02 = new ForkJoinDemo(middle, end);
//将新创建的子任务放入当前线程的workQueue队列中
task01.fork();
task02.fork();
//返回子任务及其子任务的执行结果的汇总
return task01.join() + task02.join();
}
}
}
创建并执行任务:
将Fork/Join与单线程执行及并行流式计算作对比
public class TestForkJoin {
public static void main(String[] args) {
test01();
test02();
test03();
}
public static void test01(){
long sum = 0L;
long start = System.nanoTime();
for (long i = 0; i < 1000000000; i++) {
sum += i;
}
long end = System.nanoTime();
System.out.println("for");
System.out.println("time: " + (end-start));
System.out.println("result: " + sum);
}
public static void test02(){
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask<Long> forkJoinTask = new ForkJoinDemo(1,1000000000);
long start = System.nanoTime();
Long invoke = forkJoinPool.invoke(forkJoinTask);
long end = System.nanoTime();
System.out.println("ForkJoin");
System.out.println("time: " + (end-start));
System.out.println("result: " + invoke);
}
public static void test03(){
long start = System.nanoTime();
//range() [) rangeClose() []
//parallel() 返回等效的并行流 可能返回流本身(流本身即为并行流或基础流的状态被修改为并行)
//long reduce(long identity, LongBinaryOperator op); 根据指定计算模型将Stream中的数据进行计算并返回结果(参数:计算的初始值 计算模型)计算的初始值不为0会发生线程安全问题
//long sum = LongStream.range(0, 1000000000).parallel().reduce(0, Long::sum);
long sum = LongStream.range(0, 1000000000).parallel().sum();//与上行注释中的代码等价(sum()内部调用reduce(0, Long::sum);)
long end = System.nanoTime();
System.out.println("Stream");
System.out.println("time: " + (end-start));
System.out.println("result: " + sum);
}
}