使用背景:

在实际的项目开发中,当你遇到一个大文件需要批量入库的时候,比如:文件有用户信息,商品信息,商家信息,你需要从这个文件里分批读出来分别存储,然后将这些存储到不同的数据库上。

传统做法:

我们一般是串行执行,例如:先读取用户信息、商品信息、商家信息,然后将这些信息分配存储到分别的表内

我们设想一下,如果数据量非常大,那么这个导入时间是巨大的,我们自然而然的就想到的并行执行,而这个就用到了CompletableFuture

CompletableFuture的作用:

提供了函数式编程和异步执行任务

这里介绍我常用的方法:

创建异步任务:

CompletableFuture.runAsync(...)://没有返回结果
CompletableFuture.supplyAsync(...)://有返回结果

   public static CompletableFuture<Void> runAsync(Runnable runnable,
                                                   Executor executor) {
        return asyncRunStage(screenExecutor(executor), runnable);
    }

任务回调方法:

.whenComplete(...)://无论异步任务是正常完成,还是抛出异常,都会执行这个回调;

    public CompletableFuture<T> whenComplete(
        BiConsumer<? super T, ? super Throwable> action) {
        return uniWhenCompleteStage(null, action);
    }

这里着重介绍一下runAsync()方法

static CompletableFuture<Void> runAsync(Runnable runnable);
static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor);

第一个方法使用的是默认线程池,其线程数默认等于CPU核心数,当文件数量远大于核心数时,任务会排队等待,无法充分利用IO资源

第二个是使用自定义线程池,可以自定义核心线程数量和最大线程池数量

在使用背景的情况下,是为IO密集型场景(设置线程数为CPU核心数的2-4倍),提升并行处理效率。所以通常选择第二个创建方法。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐