package com.zhoujianpeng.project.ForkJoin; import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinTask; import java.util.concurrent.RecursiveTask; /** * Fork-Join * 并行执行任务,将大任务分成小任务,最后汇总每个小任务.类似于reduce * 线程切取任务(队列) * 窃取算法,任务分成互不干涉的子任务,减少线程之间的竞争,把子任务放到不同的队列中去 * 为每个队列创建一个单独的线程,来执行队列中的任务,线程和队列是一一对应的 * 但是有的线程把自己队列中的任务已经干完,而其他线程的队列还有对应的任务等待处理 * 干完活的线程等着,不能帮其他的线程去干活,于是就去其他线程的队列中窃取任务执行 * 而这时两个线程会访问同一个队列,所以为了防止线程之间对任务的竞争的关系,通常使用 * 的是双端队列,被切取任务的线程永远从双端队列的头部拿任务执行而窃取任务的线程 * 从双端队列的尾部去拿任务执行,这个就是充分利用线程进行并行计算,减少了线程之间的 * 竞争,缺点是在某些情况下还是存在竞争的关系的,比如双端队列中只有一个任务的时候 */ /** * 拆分任务的过程 * 泛型是返回任务的结果 */ public class ForkJoinEx extends RecursiveTask<Integer> { /** * 局限性: * 只能用fork_join * 比如:任务只能用fork和join的同步机制,如果使用了其他的同步机制,那么操作 * 工作线程就不能执行其他的任务了,任务进行进入了休眠的,那么在休眠时间内 * 正在执行任务的工作线程将不在执行其他的任务 * 不能抛出 * * @param args */ public static final int threshold = 2; private int start; private int end; public ForkJoinEx(int start, int end) { this.start = start; this.end = end; } @Override protected Integer compute() { int sum = 0; //如果任务足够小,就计算任务 boolean canComputer = (end - start) <= threshold; if (canComputer) { for (int i = start; i <= end; i++) { sum += i; } } else { //如果任务大于阈值,就分裂成两个子任务进行计算 int middle = (end + start) / 2; ForkJoinEx leftTask = new ForkJoinEx(start, middle); ForkJoinEx rightTask = new ForkJoinEx(middle + 1, end); //执行子任务 leftTask.fork(); rightTask.fork(); //等待任务执行结束合并其结果 Integer leftResult = leftTask.join(); Integer rightResult = rightTask.join(); //合并子任务 sum = leftResult + rightResult; } return sum; } public static void main(String[] args) { //相当于线程池 ForkJoinPool forkJoinPool = new ForkJoinPool(); //生成一个计算任务,计算1+2+3+4 ForkJoinEx task = new ForkJoinEx(1, 100); //执行一个任务 ForkJoinTask<Integer> result = forkJoinPool.submit(task); try { Integer count = result.get(); System.out.println("count = " + count); } catch (Exception e) { e.printStackTrace(); } } }
ForkJoin任务的并行处理
最新推荐文章于 2022-11-04 09:25:41 发布