多线程中的Fork/Join框架

1.Fork/Join是什么

Fork/Join框架是Java7提供了的一个用于并行执行任务的框架, 是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。
该框架采用的是分而治之的思想,将一个难以解决的大问题,分割成相同的多个小问题,最终去一一解决;
分治策略是:对于一个规模为 n 的问题,若该问题可以容易地解决(比如说 规模 n 较小)则直接解决,否则将其分解为 k 个规模较小的子问题,这些子问题 互相独立且与原问题形式相同(子问题相互之间有联系就会变为动态规范算法), 递归地解这些子问题,然后将各子问题的解合并得到原问题的解。这种算法设计 策略叫做分治法。

2.用在哪里

在十大经典算法中,快速排序、归并排序、二分查找,还有大数据中 M/R 都是采用的分而治之的思想。
Fork/Join框架要完成两件事情:
  1.任务分割:首先Fork/Join框架需要把大的任务分割成足够小的子任务,如果子任务比较大的话还要对子任务进行继续分割
  2.执行任务并合并结果:分割的子任务分别放到双端队列里,然后几个启动线程分别从双端队列里获取任务执行。子任务执行完的结果都放在另外一个队列里,启动一个线程从队列里取数据,然后合并这些数据。

3.原理?

在这里插入图片描述
Fork/Join的工作密取:即当前线程的 Task 已经全被执行完毕,则自动取到其他线程的 Task 池中取
出 Task 继续执行。ForkJoinPool 中维护着多个线程(一般为 CPU 核数)在不断地执行 Task,每
个线程除了执行自己职务内的 Task 之外,还会根据自己工作线程的闲置情况去 获取其他繁忙的工作线程的 Task,如此一来就能能够减少线程阻塞或是闲置的时 间,提高 CPU 利用率。

在这里插入图片描述
在这里插入图片描述

4.使用的范式以及相关api

使用的标准范式:我们要使用 ForkJoin 框架,必须首先创建一个 ForkJoin 任务。它提供在任务
中执行 fork 和 join 的操作机制,通常我们不直接继承 ForkjoinTask 类,只需要直 接继承其子类。

  1. RecursiveAction,用于没有返回结果的任务
  2. RecursiveTask,用于有返回值的任务
    task 要通过 ForkJoinPool 来执行,使用 submit,execute 或 invoke 提交,两者的区
    别是:invoke 是同步执行,调用之后需要等待任务完成,才能执行后面的代码; submit(有返回值)或execute(无返回值) 是异步执行。
    invokeAll(ForkJoinTask<?> t1, ForkJoinTask<?> t2):存入两个forkjoin任务,一般用于左右任务
    invokeAll(ForkJoinTask<?>… tasks):存入一个可变长度的任务数组
    invokeAll(Collection tasks):存入一个集合
    join()和 get 方法当任务完成的时候返回计算结果。
    compute():将服任务拆分成多个子任务以及计算不可分割的子任务的结果的逻辑方法。在调用 invokeAll 方法时,又会进入 compute 方法,看看当前子任务是否需要继 续分割成孙任务,如果不需要继续分割,则执行当前子任务并返回结果。使用 join 方法会等待子任务执行完并得到其结果

5.Fork/Join的同步用法同时演示返回结果值:统计整形数组中所有元素的和

        /*阈值*/
        //当任务的个数拆分到该值时,将不再拆分
        private final static int THRESHOLD = MakeArray.ARRAY_LENGTH/10;
        //所有元素的数组
        private int[] src;
        //开始的任务索引
        private int fromIndex;
        //结束的任务索引(每次拆分,该值都会变化)
        private int toIndex;

        public SumTask(int[] src, int fromIndex, int toIndex) {
            this.src = src;
            this.fromIndex = fromIndex;
            this.toIndex = toIndex;
        }

        @Override
        protected Integer compute() {
            /*任务的大小是否合适*/
            if (toIndex - fromIndex < THRESHOLD){
//                System.out.println(" from index = "+fromIndex
//                        +" toIndex="+toIndex);
                int count = 0;
                for(int i= fromIndex;i<=toIndex;i++){
                	SleepTools.ms(1);
                     count = count + src[i];
                }
                return count;
            }else{
                //fromIndex....mid.....toIndex
                int mid = (fromIndex+toIndex)/2;
                SumTask left = new SumTask(src,fromIndex,mid);
                SumTask right = new SumTask(src,mid+1,toIndex);
                invokeAll(left,right);
                return left.join()+right.join();
            }
        }
    }


    public static void main(String[] args) {

        int[] src = MakeArray.makeArray();
        /*new出池的实例*/
        ForkJoinPool pool = new ForkJoinPool();
        /*new出Task的实例*/
        SumTask innerFind = new SumTask(src,0,src.length-1);

        long start = System.currentTimeMillis();

        pool.invoke(innerFind);
        //System.out.println("Task is Running.....");

        System.out.println("The count is "+innerFind.join()
                +" spend time:"+(System.currentTimeMillis()-start)+"ms");

    }

6.Fork/Join的异步用法同时演示不要求返回值:遍历指定目录(含子目录)寻找指定类型文件

private File path;

    public FindDirsFiles(File path) {
        this.path = path;
    }

    @Override
    protected void compute() {
        List<FindDirsFiles> subTasks = new ArrayList<>();

        File[] files = path.listFiles();
        if (files!=null){
            for (File file : files) {
                if (file.isDirectory()) {
                    // 对每个子目录都新建一个子任务。
                    subTasks.add(new FindDirsFiles(file));
                } else {
                    // 遇到文件,检查。
                    if (file.getAbsolutePath().endsWith("txt")){
                        System.out.println("文件:" + file.getAbsolutePath());
                    }
                }
            }
            if (!subTasks.isEmpty()) {
                // 在当前的 ForkJoinPool 上调度所有的子任务。
                for (FindDirsFiles subTask : invokeAll(subTasks)) {
                    subTask.join();
                }
            }
        }
    }

    public static void main(String [] args){
        try {
            // 用一个 ForkJoinPool 实例调度总任务
            ForkJoinPool pool = new ForkJoinPool();
            FindDirsFiles task = new FindDirsFiles(new File("F:/"));

            /*异步提交*/
            pool.execute(task);

            /*主线程做自己的业务工作*/
            System.out.println("Task is Running......");
            Thread.sleep(1);
            int otherWork = 0;
            for(int i=0;i<100;i++){
                otherWork = otherWork+i;
            }
            System.out.println("Main Thread done sth......,otherWork="
                    +otherWork);
            task.join();//阻塞方法(会一直阻塞在这里,直到所有子任务都执行完成)
            System.out.println("Task end");
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值