一、前言
现在大部分的CPU都是多核,我们都知道想要提升我们应用程序的运行效率,就必须得充分利用多核CPU的计算能力;Java为我们提供了大量多线程API,使用它们可以让我们的代码避免同步阻塞,进而达到提升运行效率的目的,CompletableFuture就是其中一个非常强大且重要API,下面我们就来介绍一下CompletableFuture的概念和使用。
二、概念介绍
-
CompletableFuture 是 Java 8 中新增的一个异步编程工具类,它是基于 Future 和 CompletionStage 接口构建的,可以与 Java 8 中的 Stream API 配合使用,也能够与 Java 9 中的 Reactive Stream API 进行交互。
-
主要用于异步执行任务并返回结果,实现异步计算和操作组合。它提供了一种灵活、可组合的方式来实现异步计算,同时也提供了异常处理、取消、超时等特性。在CompletableFuture中,我们可以通过回调函数来处理任务的结果,也可以使用其它方法来组合多个CompletableFuture对象,以构建更复杂的异步操作流水线。
三、自身特性
-
异步执行:CompletableFuture 可以在新的线程上异步执行计算或操作,从而不会阻塞主线程,提高程序的响应速度。
-
可组合性:CompletableFuture 的操作可以组合成一个或多个的 CompletableFuture 对象,从而构成复杂的异步计算链。
-
异常处理:CompletableFuture 可以对异常进行处理,通过 exceptionally() 方法可以捕获计算中的异常并返回默认值。
-
取消与超时:CompletableFuture 支持取消异步任务,还可以设置超时时间来避免任务的无限等待。
-
非阻塞式等待: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() | |
访问成功 | Yes | Yes | No |
访问失败 | Yes | Yes | Yes |
能从失败中恢复 | Yes | No | Yes |
能转换结果从T 到 U | Yes | No | No |
成功时触发 | Yes | Yes | No |
失败时触发 | Yes | Yes | Yes |
有异步版本 | Yes | Yes | Yes(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