Fork/Join框架原理及应用

简介原理

ForkJoin是由JDK1.7后提供多线并行处理框架。ForkJoin的框架的基本思想是分而治之。就是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果。
以下是我理解的流程原理图。
在这里插入图片描述
更加直观一点的图如下:
在这里插入图片描述
我们需要了解它的两个核心类:

  1. ForkJoinTask,就是执行具体处理逻辑的类,该类提供了在任务中执行fork和join的机制。但通常情况下我们不需要直接继承ForkJoinTask类,只需要继承它的2个子类:

RecursiveAction:用于没有返回结果的任务
RecursiveTask: 用于有返回结果的任务

2.ForkJoinPool,ForkJoinTask需要通过ForkJoinPool来执行。

通过ForkJoinPool forkJoinPool = ForkJoinPool.commonPool() 来获得,
也可通过构造,自定义设置属性来获取。一般使用前者就可以。

应用

由于是JDK提供的框架,所以无需引入其他依赖。

需求:导出业务数据库所有的用户数据。
分析:由于数据量特别大,所以查询是需要使用到多线程处理,否则一次性从mysql中捞出大量数据,会很耗性能,而且时间会很长。

首先创建分批任务类:

/**
 * @Description 用户分批查询任务
 * @Author gourd.hu
 * @Date 2020/9/28 15:33
 * @Version 1.0
 */
public class UserFindTask extends RecursiveTask<List<RbacUser>> {
    /**
     * 默认分批数量
     */
    public static final int BATCH_COUNT = 10000;
    /**
     * 分批数量
     */
    private Integer batchCount;

    /**
     * 任务最大的分批页
     */
    private Integer maxBatchNo;
    /**
     * 任务最小的分批页
     */
    private Integer minBatchNo;

    /**
     * 数据查询接口
     */
    private RbacUserDao rbacUserDao;

    public UserFindTask(Integer batchCount,Integer minBatchNo,Integer maxBatchNo,RbacUserDao rbacUserDao){
        this.batchCount = batchCount;
        this.minBatchNo = minBatchNo;
        this.maxBatchNo = maxBatchNo;
        this.rbacUserDao = rbacUserDao;
    }
    public UserFindTask(Integer minBatchNo,Integer maxBatchNo,RbacUserDao rbacUserDao){
        this.batchCount = BATCH_COUNT;
        this.minBatchNo = minBatchNo;
        this.maxBatchNo = maxBatchNo;
        this.rbacUserDao = rbacUserDao;
    }
    @Override
    protected List<RbacUser> compute() {
        // 相当于分页,当最小、最大分页数相同时,说明任务已切分完成,执行任务逻辑
        if(maxBatchNo.equals(minBatchNo)){
            Page page = new Page<>(minBatchNo,batchCount);
            LambdaQueryWrapper<RbacUser> queryWrapper = new LambdaQueryWrapper();
            queryWrapper.orderByDesc(RbacUser::getId);
            return rbacUserDao.selectPage(page,queryWrapper).getRecords();
        }
        // 将任务分成2批次
        int middleBatchNo =  (maxBatchNo+minBatchNo) >>1 ;
        // 每个批次创建各自子任务
        UserFindTask userFindTask1 = new UserFindTask(batchCount,minBatchNo,middleBatchNo,rbacUserDao);
        UserFindTask userFindTask2 = new UserFindTask(batchCount,middleBatchNo+1,maxBatchNo,rbacUserDao);
        // 子任务执行(fork)
        invokeAll(userFindTask1,userFindTask2);
        // 任务汇总
        List<RbacUser> userList1 = userFindTask1.join();
        List<RbacUser> userList2 = userFindTask2.join();
        // 自定义汇总逻辑
        userList1.addAll(userList2);
        return userList1;
    }
}

主任务调用:

    public List<RbacUser> findAll(){
        // 数据量大,采用fork/join 多线程处理
        Integer userCount = rbacUserDao.selectCount(null);
        if(userCount == null || userCount ==0){
            return null;
        }
        // 计算出最大分批数
        int maxBatchNo = (int) Math.ceil((double)userCount / 1);
        ForkJoinPool forkJoinPool = ForkJoinPool.commonPool();
        UserFindTask userFindTask = new UserFindTask(1,1,maxBatchNo,rbacUserDao);

        // execute(ForkJoinTask) 异步执行tasks,无返回值
        // forkJoinPool.execute(userFindTask);

        // submit(ForkJoinTask) 异步执行,且带Task返回值,可通过task.get 实现同步到主线程
        /*ForkJoinTask<List<RbacUser>> joinTask = forkJoinPool.submit(userFindTask);
        try {
            forkJoinPool.awaitTermination(60, TimeUnit.SECONDS);
            List<RbacUser> rbacUsers = joinTask.get();
        } catch (InterruptedException | ExecutionException e) {
            log.error("批处理查询失败",e);
        }*/

        // invoke(ForkJoinTask) 有Join, tasks会被同步到主进程
        List<RbacUser> rbacUsers = forkJoinPool.invoke(userFindTask);

        // 关闭线程池
        forkJoinPool.shutdown();
        return rbacUsers;
    }

到此,整合结束,其他业务场景也可根据实际情况应用,主要应用大数据量的插入或查询操作。当然这里也可以使用异步线程池实现此功能。

===============================================
代码均已上传至本人的开源项目
cloud-plus:https://blog.csdn.net/HXNLYW/article/details/104635673

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值