背景:ForkJoinPool的优势在于,可以充分利用多cpu,多核cpu的优势,把一个任务拆分成多个“小任务”,把多个“小任务”放到多个处理器核心上并行执行;当多个“小任务”执行完成之后,再将这些执行结果合并起来即可。这种思想值得学习。
主要参考《疯狂java讲义》
使用
Java7 提供了ForkJoinPool来支持将一个任务拆分成多个“小任务”并行计算,再把多个“小任务”的结果合并成总的计算结果。
ForkJoinPool是ExecutorService的实现类,因此是一种特殊的线程池。
使用方法:创建了ForkJoinPool实例之后,就可以调用ForkJoinPool的submit(ForkJoinTask<T> task) 或invoke(ForkJoinTask<T> task)方法来执行指定任务了。
其中ForkJoinTask代表一个可以并行、合并的任务。ForkJoinTask是一个抽象类,它还有两个抽象子类:RecusiveAction和RecusiveTask。其中RecusiveTask代表有返回值的任务,而RecusiveAction代表没有返回值的任务。
下面的UML类图显示了ForkJoinPool、ForkJoinTask之间的关系:
举例
以还行没有返回值的“大任务”(简单低打印1~300的数值)为例,程序将一个“大任务”拆分成多个“小任务”,并将任务交给ForkJoinPool来执行
/** * Project Name:Spring0725 * File Name:ForkJoinPoolAction.java * Package Name:work1201.basic * Date:2017年12月4日下午2:26:55 * Copyright (c) 2017, 深圳金融电子结算中心 All Rights Reserved. * */ package work1201.basic; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.RecursiveAction; import java.util.concurrent.TimeUnit; /** * ClassName:ForkJoinPoolAction <br/> * Function: 使用ForkJoinPool完成一个任务的分段执行 * 简单的打印0-300的数值。用多线程实现并行执行 * Date: 2017年12月4日 下午2:26:55 <br/> * @author prd-lxw * @version 1.0 * @since JDK 1.7 * @see */ public class ForkJoinPoolAction { public static void main(String[] args) throws Exception{ PrintTask task = new PrintTask(0, 300); //创建实例,并执行分割任务 ForkJoinPool pool = new ForkJoinPool(); pool.submit(task); //线程阻塞,等待所有任务完成 pool.awaitTermination(2, TimeUnit.SECONDS); pool.shutdown(); } } /** * ClassName: PrintTask <br/> * Function: 继承RecursiveAction来实现“可分解”的任务。 * date: 2017年12月4日 下午5:17:41 <br/> * * @author prd-lxw * @version 1.0 * @since JDK 1.7 */ class PrintTask extends RecursiveAction{ private static final int THRESHOLD = 50; //最多只能打印50个数 private int start; private int end; public PrintTask(int start, int end) { super(); this.start = start; this.end = end; } @Override protected void compute() { if(end - start < THRESHOLD){ for(int i=start;i<end;i++){ System.out.println(Thread.currentThread().getName()+"的i值:"+i); } }else { int middle =(start+end)/2; PrintTask left = new PrintTask(start, middle); PrintTask right = new PrintTask(middle, end); //并行执行两个“小任务” left.fork(); right.fork(); } } }