java-forkjoin框架的使用
ForkJoin是Java7提供的原生多线程并行处理框架,其基本思想是将大任务分割成小任务,最后将小任务聚合起来得到结果。fork是分解的意思, join是收集的意思. 它非常类似于HADOOP提供的MapReduce框架,只是MapReduce的任务可以针对集群内的所有计算节点,可以充分利用集群的能力完成计算任务。ForkJoin更加类似于单机版的MapReduce。
在fork/join框架中,若某个子问题由于等待另一个子问题的完成而无法继续执行。那么处理该子问题的线程会主动寻找其他尚未运行完成的子问题来执行。这种方式减少了线程的等待时间,提高了性能。子问题中应该避免使用synchronized关键词或其他方式方式的同步。也不应该是一阻塞IO或过多的访问共享变量。在理想情况下,每个子问题的实现中都应该只进行CPU相关的计算,并且只适用每个问题的内部对象。唯一的同步应该只发生在子问题和创建它的父问题之间。
Fork/Join使用两个类完成以上两件事情:
1. RecursiveAction,用于没有返回结果的任务 2. RecursiveTask,用于有返回值的任务
源码推荐查询 jdk8的
· ForkJoinPool:task要通过ForkJoinPool来执行,分割的子任务也会添加到当前工作线程的双端队列中,进入队列的头部。当一个工作线程中没有任务时,会从其他工作线程的队列尾部获取一个任务。
2个构造方法
ForkJoinPool(int parallelism) 创建一个包含parallelism个并行线程的ForkJoinPool。 ForkJoinPool() 以Runtime.availableProcessors()方法的返回值作为parallelism参数来创建ForkJoinPool。
3种方式启动
异步执行 execute(ForkJoinTask) ForkJoinTask.fork 等待获取结果 invoke(ForkJoinTask) ForkJoinTask.invoke 执行,获取Future submit(ForkJoinTask) ForkJoinTask.fork(ForkJoinTask are Futures)
异常处理:
ForkJoinTask在执行的时候可能会抛出异常,但是没办法在主线程里直接捕获异常,所以ForkJoinTask提供了isCompletedAbnormally()方法来检查任务是否已经抛出异常或已经被取消了,并且可以通过ForkJoinTask的getException方法获取异常.
getException方法返回Throwable对象,如果任务被取消了则返回CancellationException。如果任务没有完成或者没有抛出异常则返回null。
if(task.isCompletedAbnormally()) { System.out.println(task.getException()); }
然后, 代码展示
import java.util.concurrent.ForkJoinPool import java.util.concurrent.ForkJoinTask import java.util.concurrent.RecursiveTask /** * fork * 对一个大数组进行并行求和的RecursiveTask * * 大任务可以拆成小任务,小任务还可以继续拆成更小的任务,最后把任务的结果汇总合并,得到最终结果,这种模型就是Fork/Join模型。 Java7引入了Fork/Join框架,我们通过RecursiveTask这个类就可以方便地实现Fork/Join模式。 * Created by wenbronk on 2017/7/13. */ class ForkJoinTest extends RecursiveTask<Long> { static final int THRESHOLD = 100 long[] array int start int end ForkJoinTest(long[] array, int start, int end) { this.start = start this.end = end this.array = array } @Override protected Long compute() { if (end - start < THRESHOLD) { long sum = 0 for (int i = start; i < end; i++) { sum += array[i] } try { Thread.sleep(100) } catch (Exception e) { e.printStackTrace() } println String.format('compute %d %d = %d', start, end, sum) } // 对于大任务, 分多线程执行 int middle = (end + start) / 2 println String.format('split %d %d => %d %d, %d %d', start, end, start, middle, middle, end) def subtask1 = new ForkJoinTest(this.array, start, middle); def subtask2 = new ForkJoinTest(this.array, middle, end); invokeAll(subtask1, subtask2) Long subresult1 = subtask1.join() Long subresult2 = subtask2.join() Long result = subresult1 + subresult2 System.out.println("result = " + subresult1 + " + " + subresult2 + " ==> " + result); return result } public static void main(String[] args) throws Exception { // 创建随机数组成的数组: long[] array = new long[400]; // fillRandom(array); // fork/join task: ForkJoinPool fjp = new ForkJoinPool(4); // 最大并发数4 ForkJoinTask<Long> task = new ForkJoinTest(array, 0, array.length); long startTime = System.currentTimeMillis(); Long result = fjp.invoke(task); long endTime = System.currentTimeMillis(); System.out.println("Fork/join sum: " + result + " in " + (endTime - startTime) + " ms."); } }
java代码的实现
package com.wenbronk.test; import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinTask; import java.util.concurrent.RecursiveTask; /** * forkjoin的简单易用 * Created by wenbronk on 2017/7/26. */ public class CountTask extends RecursiveTask<Integer>{ private volatile static int count = 0; private int start; private int end; public CountTask(int start, int end) { this.start = start; this.end = end; } public static final int threadhold = 2; @Override protected Integer compute() { int sum = 0; System.out.println("开启了一条线程单独干: " + count++); // 如果任务足够小, 就直接执行 boolean canCompute = (end - start) <= threadhold; if (canCompute) { for (int i = start; i <= end; i++) { sum += i; } }else { //任务大于阈值, 分裂为2个任务 int middle = (start + end) / 2; CountTask countTask1 = new CountTask(start, middle); CountTask countTask2 = new CountTask(middle + 1, end); // 开启线程 // countTask1.fork(); // countTask2.fork(); invokeAll(countTask1, countTask2); Integer join1 = countTask1.join(); Integer join2 = countTask2.join(); // 结果合并 sum = join1 + join2; } return sum; } // 测试 public static void main(String[] args) throws ExecutionException, InterruptedException { ForkJoinPool forkJoinPool = new ForkJoinPool(); CountTask countTask = new CountTask(1, 100); ForkJoinTask<Integer> result = forkJoinPool.submit(countTask); System.out.println(result.get()); } }