并发编程(6) —— forkjoin

本文深入探讨了并行编程中的核心概念,包括分而治之和工作窃取(workStealing)策略,通过具体实例展示了如何利用Java的Fork/Join框架实现RecursiveTask和RecursiveAction,以提高大规模数据处理的效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上一篇:并发编程(5)—— 常用方法

一、概述

forkjion里面的两个重要的概念

分而治之,什么是分而治之?
一个大的事情平均分成几个相同小的事情,如果没有达到最小的要求,就继续分,一直分到达到的要求。
工作密取,workStealing
如果分而治之的线程很多,就会形成线程的队列,这里面就会有完成的快和慢的不同线程。快的线程执行完成以后,就会把慢的队列拿过来进行执行。

在这里插入图片描述
RecursiveAction:无返回值的任务,通常用于只fork不join的情形。
RecursiveTask:有返回值的任务,通常用于fork+join的情形。

二、代码实现

RecursiveTask有返回值的任务

//计算长度为ARRAY_LENGTH  数组中的数值总和
public class TestForkJoin {

    //数组长度
    public static final int ARRAY_LENGTH  = 100000000;

    public static class MyTask extends RecursiveTask<Integer>{

        private final static int THRESHOLD = MakeArray.ARRAY_LENGTH/10;
        private int[] src; //表示我们要实际统计的数组
        private int fromIndex;//开始统计的下标
        private int toIndex;//统计到哪里结束的下标

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

        @Override
        protected Integer compute() {
            if(toIndex - fromIndex < THRESHOLD){
                int count = 0;
                for (int i = fromIndex; i <= toIndex ; i++) {
                    count = count + src[i];
                }
                return count;
            }else{
                int mid = (fromIndex + toIndex) / 2;
                MyTask left = new MyTask(src,fromIndex,mid);
                MyTask right = new MyTask(src,mid+1,toIndex);
                invokeAll(left,right);
                return left.join()+right.join();
            }
        }


        public static void main(String[] args) {
        	//使用forkJoin
            ForkJoinPool forkJoinPool = new ForkJoinPool();
            int[] src = makeArray();
            MyTask myTask = new MyTask(src,0,src.length-1);
            long currentTimeMillis = System.currentTimeMillis();
            Integer invoke = forkJoinPool.invoke(myTask);//同步调用
            System.out.println("forkjoin结果是: "+invoke+"  耗时:"+(System.currentTimeMillis() - currentTimeMillis)+"毫秒");


			//使用普通遍历求和
            long start = System.currentTimeMillis();
            int sum= 0;
            for(int i= 0;i<src.length;i++){
                sum = sum + src[i];
            }
            System.out.println("普通遍历结果是: "+sum
                    +"  耗时:"+(System.currentTimeMillis()-start)+"毫秒");
        }

        //创建随机数数组
        public static int[] makeArray() {
            //new一个随机数发生器
            Random r = new Random();
            int[] result = new int[ARRAY_LENGTH];
            for(int i=0;i<ARRAY_LENGTH;i++){
                //用随机数填充数组
                result[i] =  r.nextInt(ARRAY_LENGTH*3);
            }
            return result;

        }

    }

}

运行结果:当数组长度比较大的时候,使用forkjoin总是比普通遍历求和要快

forkjoin结果是: -872683364  耗时:45毫秒
普通便利结果是: -872683364  耗时:58毫秒

Process finished with exit code 0

RecursiveAction无返回值的任务

public class ForkJoinTest extends RecursiveAction {

    private File file;

    public ForkJoinTest(File file){
        this.file = file;
    }

    public static void main(String[] args){
        ForkJoinPool pool =new ForkJoinPool();
        ForkJoinTest task = new ForkJoinTest(new File("D:\\wanglin\\wangl"));
        pool.execute(task);//异步调用加入任务
        System.out.println("Task is start");
        task.join();//阻塞(因为主线程运行完forkjoin还未运行完成就会被终止)
        System.out.println("Tash is end");
    }

    @Override
    protected void compute() {
        List<ForkJoinTest> subTasks = new ArrayList<>();
        File[] files = file.listFiles();
        for(File file:files){
            if(file.isDirectory()){
                subTasks.add(new ForkJoinTest(file));
            }else {
                System.out.println(file.getAbsolutePath());
            }
        }
        if(!subTasks.isEmpty()){
            for(ForkJoinTest task:invokeAll(subTasks)){
                task.join();
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值