java多线程分段捞取数据

  1. 背景
    从一个数据量约十多亿的表中,捞出符合时间范围内所需的数据

  2. 解决方案
    在Java8中,CompletableFuture提供了非常强大的Future的扩展功能,可以帮助我们简化异步编程的复杂性,并且提供了函数式编程的能力,可以通过回调的方式处理计算结果,也提供了转换和组合 CompletableFuture 的方法。
    它可能代表一个明确完成的Future,也有可能代表一个完成阶段( CompletionStage ),它支持在计算完成以后触发一些函数或执行某些动作。
    它实现了Future和CompletionStage接口

根据id分组取模,多线程分段查询,最后进行归并

创建一个并行supplier工具类,方便调用或以后复用

public class ParallelSupplierUtils {

    /** 线程池 */
    private static final ThreadPoolTaskExecutor EXECUTOR = new VisiableThreadPoolTaskExecutor();

    static {
        // 配置核心线程数
        EXECUTOR.setCorePoolSize(16);
        // 配置最大线程数
        EXECUTOR.setMaxPoolSize(160);
        // 配置队列大小
        EXECUTOR.setQueueCapacity(5000);
        // 配置线程池中的线程的名称前缀
        EXECUTOR.setThreadNamePrefix("parallel_supplier_utils-");
        // 拒绝策略:不在新线程中执行任务,而是用调用者所在的线程来执行
        EXECUTOR.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 等待任务在关机时完成--表明等待所有线程执行完
        EXECUTOR.setWaitForTasksToCompleteOnShutdown(true);
        // 等待时间:30分钟后强制停止
        EXECUTOR.setAwaitTerminationSeconds(60 * 30);
        // 执行初始化
        EXECUTOR.initialize();
        log.info("初始化ParallelSupplierUtils的线程池完毕:corePoolSize={},maxPoolSize={}",
                EXECUTOR.getCorePoolSize(), EXECUTOR.getMaxPoolSize());
    }

    @Data
    public static class Holder<FIRST, SECOND, THIRD> {
        private FIRST first;
        private SECOND second;
        private THIRD third;
    }

    /**
     * 并行执行三个Supplier(为空请写null),并返回结果
     *
     * @param s1 第一个Supplier
     * @param s2 第二个Supplier
     * @param s3 第三个Supplier
     * @param <FIRST> 第一个Supplier的返回值类型
     * @param <SECOND> 第二个Supplier的返回值类型
     * @param <THIRD> 第三个Supplier的返回值类型
     * @return Holder:通过getXxx()进行取值
     */
    public static <FIRST, SECOND, THIRD> Holder<FIRST, SECOND, THIRD> supplyAsync(Supplier<FIRST> s1, Supplier<SECOND> s2, Supplier<THIRD> s3) {
        // 创建任务返回值包装对象
        Holder<FIRST, SECOND, THIRD> holder = new Holder();
        List<CompletableFuture> completableFutureList = new ArrayList<>(3);

        // 分别让任务异步执行
        if (Objects.nonNull(s1)) {
            completableFutureList.add(CompletableFuture.supplyAsync(s1, EXECUTOR).thenAccept(holder::setFirst));
        }
        if (Objects.nonNull(s2)) {
            completableFutureList.add(CompletableFuture.supplyAsync(s2, EXECUTOR).thenAccept(holder::setSecond));
        }
        if (Objects.nonNull(s3)) {
            completableFutureList.add(CompletableFuture.supplyAsync(s3, EXECUTOR).thenAccept(holder::setThird));
        }

        // 等待全部任务执行完毕
        CompletableFuture[] cfs = completableFutureList.toArray(new CompletableFuture[completableFutureList.size()]);
        CompletableFuture.allOf(cfs).join();

        // 返回结果
        return holder;
    }

    /**
     * 执行一系列相同返回值类型的Supplier型函数,并收集返回值
     *
     * @param supplierList Supplier型函数集合
     * @param <R> Supplier型函数的返回值类型
     * @return 返回值Set
     */
    public static <R> Set<R> supplyAsync(List<Supplier<R>> supplierList) {
        if (CollectionUtils.isEmpty(supplierList)) {
            return Collections.emptySet();
        }

        // 分别让任务异步执行
        Set<R> resultSet = new HashSet<>(supplierList.size());
        List<CompletableFuture> completableFutureList = supplierList.stream().filter(Objects::nonNull).map(s ->
            CompletableFuture.supplyAsync(s, EXECUTOR).thenAccept(r -> {
                if (Objects.nonNull(r)) {
                    resultSet.add(r);
                }
            })
        ).collect(Collectors.toList());

        // 等待全部任务执行完毕
        CompletableFuture[] cfs = completableFutureList.toArray(new CompletableFuture[completableFutureList.size()]);
        CompletableFuture.allOf(cfs).join();

        // 返回结果
        return resultSet;
    }

    /**
     * 执行一系列相同返回值类型的Supplier型函数,并收集返回值
     *
     * @param supplierList Supplier型函数集合
     * @param <R> Supplier型函数的返回值类型
     * @return 结果
     */
    public static <R> List<R> supplyAsyncWithListRes(List<Supplier<R>> supplierList) {
        if (CollectionUtils.isEmpty(supplierList)) {
            return Collections.emptyList();
        }

        // 分别让任务异步执行
        List<R> resultList = new ArrayList<>(supplierList.size());

        // 等待全部任务执行完毕
        CompletableFuture[] cfs = supplierList.stream().filter(Objects::nonNull).map(s ->
                CompletableFuture.supplyAsync(s, EXECUTOR).thenAccept(r -> {
                    if (Objects.nonNull(r)) {
                        resultList.add(r);
                    }
                })
        ).toArray(CompletableFuture[]::new);
        CompletableFuture.allOf(cfs).join();

        // 返回结果
        return resultList;
    }
}

可根据实际情况,动态配置参与并行线程数

  List<Supplier<List<T>>> supplierList = Lists.newArrayList();
        for (int i = 0; i < threadNum; i++) {
            int threads = threadNum;
            int currentMod = i;
            supplierList.add(() -> xxxMapper.selectxxx( threads, currentMod));
        }
        List<List<T>> rspList = ParallelSupplierUtils.supplyAsyncWithListRes(supplierList);
        List<T> result = Lists.newArrayList();
        rspList.forEach(result::addAll);
    <select id="selectxxx" resultType="xxx">
    SELECT *  FROM  A a
        WHERE
        <if test="threadsNum > 1">
            AND a.id % #{threadsNum} = #{currentMod}
        </if>
    </select>
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值