JUC---fork-join框架介绍

本文介绍ForkJoinPool的原理,适用于计算密集型场景的并行计算框架。通过自定义任务,利用分治思想将大任务拆分为小任务并行处理,最后汇总结果。文章提供计算求和和文件查找的代码示例。

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

目录

1.原理

2.使用场景

3.使用范式

3.1 定义ForkJoinPool

3.2 自定义任务

3.3 执行任务

3.4  获取结果

4.代码示例

4.1 计算

4.2 文件查找

5.注


1.原理

将一个大任务拆分(fork)成若干个小任务,再将小任务运算的结果进行(join)汇总。(分而自治的思想)

2.使用场景

适合计算密集型场景;

3.使用范式

3.1 定义ForkJoinPool

ForkJoinPool pool = new ForkJoinPool();

定义ForkJoinPool,默认线程个数为CPU核数。

3.2 自定义任务

 ForkJoinTest forkJoinTest = new ForkJoinTest(1, 100000000);

继承RecursiveTask有返回值;

继承RecursiveAction无返回值;

3.3 执行任务

 pool.submit(forkJoinTest) &&  pool.invoke(forkJoinTest)

invoke是同步执行,submit是异步执行。

3.4  获取结果

forkJoinTest.get(),阻塞方法。

4.代码示例

4.1 计算

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;

/**
 * 计算1+2+3+4+.....的值
 */
public class ForkJoinTest extends RecursiveTask<Integer> {
    public static int standard = 2;
    private int start;
    private int end;
    private int sum = 0;
    public ForkJoinTest(int start, int end) {
        this.start = start;
        this.end = end;
    }
    //计算
    @Override
    protected Integer compute() {
        //当每组的数值个数小于等于standard 2 时,进行累加操作
        boolean flag = start - end <= standard;
        if (flag) {
            for (int i = start; i <= end; i++) {
                sum += i;
            }
        } else {
            //二分 
            int mid = (start + end) / 2;
            ForkJoinTest left = new ForkJoinTest(start, mid);
            ForkJoinTest right = new ForkJoinTest(mid + 1, end);
            // invokeAll(left, right);
            left.fork(); //拆分计算
            right.fork();//拆分计算
            sum = left.join() + right.join();//合并计算结果值
        }
        return sum;
    }

    public static void main(String[] args) throws Exception {
        //定义ForkJoinPool
        ForkJoinPool pool = new ForkJoinPool();
        //自定义任务
        ForkJoinTest forkJoinTest = new ForkJoinTest(1, 100000000);
        //同步执行任务
        // pool.invoke(forkJoinTest);
        //异步执行任务
        pool.submit(forkJoinTest);
        //获取结果
        System.out.println(forkJoinTest.get());

    }
}

4.2 文件查找

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;

/**
 * 查找文件中.txt后缀的文件,并打印出来
 */
public class ForkJoinTest2 extends RecursiveAction {
    private File file;
    //用于封装子任务
    public List<ForkJoinTest2> list = new ArrayList<>();

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

    @Override
    protected void compute() {
        File[] files = file.listFiles();
        if (files != null) {
            for (File f : files) {
                if (f.isDirectory()) {
                    //为每一个目录创建一个子任务
                    list.add(new ForkJoinTest2(f));
                } else {
                    //遇到文件则检查,打印出后缀为.txt的文件
                    if (f.getAbsolutePath().endsWith(".txt")) {
                        System.out.println(f.getAbsolutePath());
                    }
                }
            }
            if (!list.isEmpty()) {//在当前的ForkJoinPool调度所有的子任务
                invokeAll(list);
                for (ForkJoinTest2 forkJoinTest2 : list) {
                    forkJoinTest2.join();
                }
                //invokeAll效率要比逐个fork高得多
            /*   for(ForkJoinTest2 forkJoinTest2:list){
                    forkJoinTest2.fork();
                }
                for(ForkJoinTest2 forkJoinTest2:list){
                    forkJoinTest2.join();
                }*/

            }
        }


    }

    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        ForkJoinPool pool = new ForkJoinPool();
        ForkJoinTest2 forkJoinTest2 = new ForkJoinTest2(new File("D:\\PCIdownload"));//尽量找层级深的那个目录测试
        pool.invoke(forkJoinTest2);
        long endTime = System.currentTimeMillis();
        System.out.println(endTime - startTime);
        findTxt(new File("D:\\PCIdownload"));
        System.out.println(System.currentTimeMillis() - endTime);
    }

    public static void findTxt(File file) {
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            if (files != null) {
                for (File file1 : files) {
                    findTxt(file1);
                }
            }

        }else {
            if(file.getAbsolutePath().endsWith(".txt")){
                System.out.println(file.getAbsolutePath());
            }
        }

    }
}

5.注

1.ForkJoinPool的每个工作线程维护这一个工作队列(workQueue);新的任务放在队尾,消费时从队尾获取;其他线程从队头窃取工作任务。

2.工作窃取机制;本线程任务执行完,会去窃取别的线程的任务;而不像线程池+countDownLatch;线程执行完当前任务后会阻塞。

3.invokeAll()方法性能比fork方法更好。Pool里面线程数量是固定的,那么调用子任务的fork方法相当于A先分工给B,然后A当监工不干活,B去完成A交代的任务。所以上面的模式相当于浪费了一个线程。那么如果使用invokeAll相当于A分工给B后,A和B都去完成工作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值