【Java多线程】ForkJoinPool

来源:互联网 发布:阿里云服务器多台 编辑:程序博客网 时间:2024/06/06 13:59

1.定义

Fork/Join框架是Java7提供了的一个用于并行执行任务的框架, 是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架

2.与线程池的区别

(1)采用工作窃取模式(work-stealing):某个线程从其他队列里窃取任务来执行。

为了减少窃取任务线程和被 窃取任务线程之间的竞争,通常会使用双端队列,被窃取任务线程永远从自己双端队列的头部拿 任务执行,而窃取任务的线程永远从别人双端队列的尾部拿任务执行。

(2)需要实现compute方法(RecursiveTask类的抽象方法),而RecursiveTask类继承于ForkJoinTask类,在这个方法里,首先需要判断任务是否足够小,如果足够小就执行任务。如果不足够小,就必须分割 成两个子任务,每个子任务在调用fork方法时,又会进入到compute方法。使用join方法会等待子任务执行完并得到其结果。

当执行新的任务时它可以将其拆分分成更小的任务执行,并将小任务加到线程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列中。

(3)fork/join框架的优势体现在对其中包含的任务的处理方式上.在一般的线程池中,如果一个线程正在执行的任务由于某些原因无法继续运行,那么该线程会处于等待状态。而在fork/join框架实现中,如果某个子问题由于等待另外一个子问题的完成而无法继续运行。那么处理该子问题的线程会主动寻找其他尚未运行的子问题来执行.这种方式减少了线程的等待时间,提高了性能。

(4)它同ThreadPoolExecutor一样,也实现了Executor和ExecutorService接口。它使用了一个无限队列来保存需要执行的任务,而线程的数量则是通过构造函数传入,如果没有向构造函数中传入希望的线程数量,那么当前计算机可用的CPU数量会被设置为线程数量作为默认值。

图解:

这里写图片描述

3.使用Fork/join框架的步骤

(1) ForkJoinTask : 创建一个ForkJoin任务,可以继承子类 : RecursiveAction:用于没有返回结果的任务。RecursiveTask:用于有返回结果的任务。

(2)ForkJoinPool:ForkJoinTask需要通过ForkJoinPool来执行。 任务分割出的子任务会添加到当前工作线程所维护的双端队列中,进入队列的头部。当 一个工作线程的队列里暂时没有任务时,它会随机从其他工作线程的队列的尾部获取一个任务。

执行方法:

//直接获取结果Long sum = pool.invoke(task);//通过Future接口获取结果Future<Long> future = pool.submit(task);Long sum = future.get();

4.实例

public class ForkJoinPoolTest {    public static void main(String[] args) {        Instant start = Instant.now();        ForkJoinPool pool = new ForkJoinPool();        ForkJoinTask<Long> task = new ForkJoinSumCalculate(0L, 50000000L);        //获取结果法一        Long sum = pool.invoke(task);        //法二        Future<Long> future = pool.submit(task);        try {            System.out.println("Future  : " + future.get());        } catch (InterruptedException e) {            e.printStackTrace();        } catch (ExecutionException e) {            e.printStackTrace();        }        System.out.println("计算结果:" + sum);        Instant end = Instant.now();        System.out.println("耗费时间为:" + Duration.between(start, end).toMillis());//166-1996-10590    }}class ForkJoinSumCalculate extends RecursiveTask<Long>{    private static final long serialVersionUID = -259195479995561737L;    private long start;    private long end;    private static final long THURSHOLD = 10000L;  //临界值    public ForkJoinSumCalculate(long start, long end) {        this.start = start;        this.end = end;    }    @Override    protected Long compute() {        long length = end - start;        if(length <= THURSHOLD){            long sum = 0L;            for (long i = start; i <= end; i++) {                sum += i;            }            return sum;        }else{            long middle = (start + end) / 2;            ForkJoinSumCalculate left = new ForkJoinSumCalculate(start, middle);            left.fork(); //进行拆分,同时压入线程队列            ForkJoinSumCalculate right = new ForkJoinSumCalculate(middle+1, end);            right.fork(); //            return left.join() + right.join();        }    }}

输出结果:

Future  : 1250000025000000计算结果:1250000025000000耗费时间为:99



本人才疏学浅,若有错误,请指出
谢谢!

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 手机文件打开是乱码怎么办 手机wps文件打开是乱码怎么办 腾讯视频vip账号被盗怎么办 附单据数错了 怎么办 橡胶的回弹性差怎么办 自己喷漆喷坏了怎么办 透明塑料磨花了怎么办 包包金属刮花了怎么办 鞋子刮了黑印子怎么办 黑色鞋跟磨白了怎么办 脚穿鞋子磨起泡怎么办 脚被鞋子磨红了怎么办 脚被鞋子磨黑了怎么办 白鞋皮鞋磨了皮怎么办 小脚趾磨肿了怎么办 穿鞋小拇指磨脚怎么办 高铁东西忘了怎么办 人故意去撞车死了怎么办? 新货车上户超重怎么办 车险出保单车号填错怎么办 货车拦板变形了怎么办 行车监控看不清楚车号怎么办? 1.5米的鱼缸要怎么办 被锤子砸到手了怎么办 家里地下污水管道堵塞怎么办 家里pvc灯罩变黄怎么办 欧普吸顶灯灯罩坏了怎么办 硬盘用久了变慢怎么办 地税申报工资人员弄错怎么办 买保险保单丢了怎么办 买保险的银行卡丢了怎么办 没学过JAVA入职怎么办 磨砂皮擦了鞋油怎么办 磨破皮伤口有沙子怎么办 工行信用卡被风险锁定了怎么办 超重被超限站查住以后怎么办 银行卡输入密码次数超限怎么办 信用卡密码错误次数超限怎么办 农行密码错误次数超限怎么办 剪力墙偏心受拉怎么办 韵达快递寄丢了怎么办