异步编排:CompletableFuture使用

一、前言

现在大部分的CPU都是多核,我们都知道想要提升我们应用程序的运行效率,就必须得充分利用多核CPU的计算能力;Java为我们提供了大量多线程API,使用它们可以让我们的代码避免同步阻塞,进而达到提升运行效率的目的,CompletableFuture就是其中一个非常强大且重要API,下面我们就来介绍一下CompletableFuture的概念和使用。

二、概念介绍

  • CompletableFuture 是 Java 8 中新增的一个异步编程工具类,它是基于 Future 和 CompletionStage 接口构建的,可以与 Java 8 中的 Stream API 配合使用,也能够与 Java 9 中的 Reactive Stream API 进行交互。

  • 主要用于异步执行任务并返回结果,实现异步计算和操作组合。它提供了一种灵活、可组合的方式来实现异步计算,同时也提供了异常处理、取消、超时等特性。在CompletableFuture中,我们可以通过回调函数来处理任务的结果,也可以使用其它方法来组合多个CompletableFuture对象,以构建更复杂的异步操作流水线。

三、自身特性

  1. 异步执行:CompletableFuture 可以在新的线程上异步执行计算或操作,从而不会阻塞主线程,提高程序的响应速度。

  2. 可组合性:CompletableFuture 的操作可以组合成一个或多个的 CompletableFuture 对象,从而构成复杂的异步计算链。

  3. 异常处理:CompletableFuture 可以对异常进行处理,通过 exceptionally() 方法可以捕获计算中的异常并返回默认值。

  4. 取消与超时:CompletableFuture 支持取消异步任务,还可以设置超时时间来避免任务的无限等待。

  5. 非阻塞式等待:CompletableFuture 提供了非阻塞式的等待方法,如 join() 和 getNow() 方法。

四、使用方式

1、异步执行一个任务并获取结果

通过 CompletableFuture 的静态方法 supplyAsync() 可以异步执行一个任务,返回 CompletableFuture 对象,通过该对象可以获取任务执行的结果。

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    // 执行一些耗时的操作
    return "Hello CompletableFuture";
});
String result = future.get(); // 阻塞等待任务执行完成并获取结果
System.out.println(result);
2、异步执行一个任务并处理异常

CompletableFuture 提供了方法 handle() 来处理异步任务执行过程中的异常,它可以处理任务完成时的异常,也可以处理任务执行过程中的异常。

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    // 执行一些可能会出现异常的操作
    throw new RuntimeException("Something went wrong");
}).handle((result, exception) -> {
    if (exception != null) {
        System.out.println("Task failed with exception: " + exception);
        return "default value";
    } else {
        return result;
    }
});
String result = future.get();
System.out.println(result);
3、异步执行多个任务并合并结果

通过 CompletableFuture 的静态方法 allOf() 可以并行执行多个任务,等待所有任务完成后,通过 CompletableFuture.join() 方法合并所有任务的结果。

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "CompletableFuture");
CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> "Java");
 
CompletableFuture<Void> combinedFuture = CompletableFuture.allOf(future1, future2, future3);
combinedFuture.get();
 
String result = Stream.of(future1, future2, future3)
        .map(CompletableFuture::join)
        .collect(Collectors.joining(" "));
System.out.println(result);

CompletableFuture 提供了三种方法来处理它们:handle()、whenComplete() 和 exceptionly()。这里就不做详细介绍了,有兴趣的朋友们可以自己去详细了解一下,以下为各种处理方式的比较结果。 

handle()whenComplete()exceptionly()
访问成功YesYesNo
访问失败YesYesYes
能从失败中恢复YesNoYes
能转换结果从T 到 UYesNoNo
成功时触发YesYesNo
失败时触发YesYesYes
有异步版本YesYesYes(12版本)
4、异步执行多个任务并处理其中一个任务的结果

通过 CompletableFuture 的静态方法 anyOf() 可以并行执行多个任务,只要有一个任务完成,就会立即返回其结果。

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "Result 1";
});
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "Result 2");
CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> "Result 3");
 
CompletableFuture<Object> anyOfFuture = CompletableFuture.anyOf(future1, future2, future3);
 
Object result = anyOfFuture.get();
System.out.println(result);
5、串行执行多个任务

通过 CompletableFuture 的方法 thenApply()、thenAccept() 和 thenRun() 可以串行执行多个任务,每个任务在前一个任务完成后才会执行。

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = future1.thenApply(result -> result + " CompletableFuture");
CompletableFuture<Void> future3 = future2.thenAccept(result -> System
6、 检查异步任务是否执行完成和执行回调

在CompletableFuture中,可以使用isDone()方法来检查异步任务是否已经执行完毕。该方法会返回一个boolean类型的值,表示异步任务是否已经完成。如果异步任务已经完成,则可以通过调用get()方法获取其返回值;如果异步任务还没有完成,则可以通过注册回调函数来等待其完成。

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    // 异步任务
    return "Hello, World!";
});
 
// 检查异步任务是否已经完成
if (future.isDone()) {
    // 获取异步任务的返回值
    String result = future.get();
    System.out.println(result);
} else {
    // 注册回调函数,在异步任务完成时获取其返回值
    future.thenAccept(result -> {
        System.out.println(result);
    });
}

五、接口API说明

​ 常用的方法

thenApply():当 CompletableFuture 完成时,执行指定的函数并返回一个新的 CompletableFuture,该函数接收前一个 CompletableFuture 的结果,并返回新的结果。
thenAccept():当 CompletableFuture 完成时,执行指定的操作但不返回任何结果,该操作接收前一个 CompletableFuture 的结果。
thenRun():当 CompletableFuture 完成时,执行指定的操作并返回一个新的 CompletableFuture。
exceptionally():在 CompletableFuture 异常时,执行指定的函数,该函数将异常转换为正常结果或抛出一个新的异常。
whenComplete():在 CompletableFuture 完成时执行指定的操作,该操作接收前一个 CompletableFuture 的结果或异常。
handle():当 CompletableFuture 完成时,执行指定的函数,该函数可以处理正常结果或异常,并返回一个新的结果。
thenCompose():在 CompletableFuture 完成时,执行指定的函数并返回一个新的 CompletableFuture,该函数接收前一个 CompletableFuture 的结果,并返回一个新的 CompletableFuture。

常用的静态方法

CompletableFuture.runAsync():在异步任务中执行没有返回值的操作。
CompletableFuture.supplyAsync():在异步任务中执行有返回值的操作。
CompletableFuture.completedFuture():创建一个已经完成的 CompletableFuture 实例。
CompletableFuture.anyOf():返回一个 CompletableFuture,其结果是给定 CompletableFuture 中最快完成的一个。
CompletableFuture.allOf():返回一个 CompletableFuture,其结果是给定 CompletableFuture 中所有任务完成后的结果。
CompletableFuture.exceptionally():在 CompletableFuture 异常时,执行指定的函数。
CompletableFuture.thenApply():在 CompletableFuture 完成后,执行指定的函数并返回一个新的 CompletableFuture。
CompletableFuture.thenAccept():在 CompletableFuture 完成后,执行指定的操作但不返回任何结果。
CompletableFuture.thenRun():在 CompletableFuture 完成后,执行指定的操作并返回一个新的 CompletableFuture。
CompletableFuture.thenCompose():在 CompletableFuture 完成后,执行指定的函数并返回一个新的 CompletableFuture。

链式调用示例

    CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 2)
            .thenApplyAsync(x -> x * 3)
            .thenApplyAsync(x -> x + 1)
            .thenApplyAsync(x -> x * 2);
    future.thenAccept(System.out::println);

使用thenApplyAsync方法将一系列任务串联起来,并在最后使用thenAccept方法处理最终的结果

组合线程池调用示例

// 创建一个自定义的线程池
ExecutorService executor = Executors.newFixedThreadPool(10);
// 使用CompletableFuture提交一个异步任务到线程池中
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
    // 异步任务的代码
}, executor);

// 阻塞等待异步任务完成
future.get();

// 关闭线程池
executor.shutdown();

六、自定义线程池实现

以下是基于SpringBoot实现:

MyThreadPoolExecutor.class文件

package com.wangzt.gulimall.product.ThreadPool;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.*;

@Configuration
public class MyThreadPoolExecutor {
    @Bean
    public ThreadPoolExecutor threadPoolExecutor(MyThreadPoolProperties pool) {
        /**
         * int corePoolSize,
         * int maximumPoolSize,
         * long keepAliveTime,
         * TimeUnit unit,
         * BlockingQueue<Runnable> workQueue,
         * ThreadFactory threadFactory,
         * RejectedExecutionHandler handler
         */
        return new ThreadPoolExecutor(pool.getCoreSize(),
                pool.getMaxSize(),
                pool.getKeepAliveTime(),
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(100000),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
    }
}

MyThreadPoolProperties.class文件

package com.wangzt.gulimall.product.ThreadPool;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "gulimall.thread")
@Data
public class MyThreadPoolProperties {
    private Integer coreSize;
    private Integer maxSize;
    private Integer keepAliveTime;
}

application.properties文件,具体参数根据实际情况而定,作为参考

# 配置gulimall 线程池
gulimall.thread.core-size=20
gulimall.thread.max-size=200
gulimall.thread.keep-alive-time=10

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值