fork/join框架

  分而治之    在必要情况下,将一个大任务拆分(fork)成若干个相同的小任务(拆分到不可拆分时),再将一个个的小任务运算的结果进行join汇总

  动态规划    分割成相同的小问题,但是小问题之间有关联,比如要执行完a,得到的结果去执行b

工作密取

当一个线程的任务完成时,会从另一个线程的任务链表尾部取一个任务去执行,执行完毕之后再放回原处,充分利用了cpu,可以充分调节线程之间的工作量,加快大任务的工作进度

 

实例:

public class InsertionSort{
    /**
     * 此处要统计所有数的和,因此需要返回值,所以此处继承RecursiveTask而不继承RecursiveAction
     *  RecursiveAction不支持返回值操作
     *
     *  静态内部类创建对象的时候,独立于外部类及其对象,就好像它是一个独立的类,可以和外部类一样使用。
     * 内部类创建对象的时候,不能独立于外部类,必须要先创建外部类的对象,然后再用这个对象来new出内部类的对象。
     *
     * 方法递归调用,在纯计算以及计算密集型场景下,少量数据比单线程慢,随着数据增加,效率慢慢提高
     * 当每一个任务需要耗时较长的操作时,fork/join的优势开始显现
     */
    private static class SumTask extends RecursiveTask<Integer> {
        //拆分的阈值,即拆分到这么个大小就停止拆分
        private static final int THRESHOLD = 40;
        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(fromIndex+"---"+toIndex);
                int count = 0;
                for (int i=fromIndex;i<=toIndex;i++){
                    //模拟任务中的耗时操作
                    try {
                        Thread.sleep(1);
                    }catch (Exception e){}
                    count = count + src[i];
                }
                return count;
            }else{//如果数组长度不小于阈值,则继续拆分,此处拆分成2个
                int mid = (toIndex+fromIndex)/2;
                //左右两个数组分别创建任务
                SumTask left = new SumTask(src,fromIndex,mid);
                SumTask right = new SumTask(src,mid+1,toIndex);
                //创建任务
                invokeAll(left,right);
                //join 阻塞调用此方法的线程直到线程执行结束
                return left.join() + right.join();
            }
        }
    }

    public static void main(String[] a){
        int[] array = MakeArray.makeArray(0);
        //创建fork/join池的实例
        ForkJoinPool pool = new ForkJoinPool();
        //创建任务实例
        SumTask task = new SumTask(array,0,array.length-1);
        long start = System.currentTimeMillis();
        //同步提交
        pool.invoke(task);
        //异步提交无返回结果
        //pool.execute(task);
        //异步提交有返回结果
        //pool.submit(task);
        //不需要返回值的话直接task.join()就可以,不需要接收返回值
        //获取结果    join 阻塞调用此方法的线程直到线程执行结束才继续执行
        System.out.println("result= "+task.join());
        System.out.println("fork/join Time = "+(System.currentTimeMillis()-start));
        int count = 0;
        start = System.currentTimeMillis();
        for(int i= 0;i<array.length;i++){
            try {
                Thread.sleep(1);
            }catch (Exception e){}
            count = count + array[i];
        }
        System.out.println("result= "+count);
        System.out.println("Time = "+(System.currentTimeMillis()-start));
    }
}



/**
 * 工具类,生成指定长度的随机数组
 */
public class MakeArray{
    public static final int ARRAY_LENGTH = 7000;//生成数组的长度
    public static final int THRESHOLD = 40;//数组拆分的阈值

    public static int[] makeArray(Integer length){
        if(length==null || length<=0){
            length = ARRAY_LENGTH;
        }
        int array[] = new int[length];
        Random r = new Random();
        for(int i=0;i<length;i++){
            array[i] = r.nextInt(100);
        }
        return array;
    }

}

上面例子中,当注释掉Thread.sleep(1)(即耗时操作)时,结果分别为

result= 345556
fork/join Time = 8
result= 345556
Time = 0

明显fork/join在纯计算型任务时不占优势

当解开注释时,结果为

result= 344834
fork/join Time = 1779
result= 344834
Time = 9513

此时,fork/join的优势开始展现出来

最后修改于 2019-08-23 22:09:26
如果觉得我的文章对你有用,请随意赞赏
扫一扫支付
上一篇