一、@Async简介与基本使用
1. 简介
@Async 和 CompletableFuture 是实现异步处理的强大工具组合。@Async 是Spring框架提供的一个注解,用于标记方法以表明它将在Spring管理的线程池中的另一个线程上异步执行。这使得开发人员能够在不阻塞主线程的情况下执行耗时的任务,从而提高应用程序的整体性能和响应速度。
CompletableFuture 是Java 8引入的一个强大的类,它代表了一个可能尚未完成的计算的结果。CompletableFuture 提供了丰富的API来支持异步编程模式,如回调、组合操作、错误处理等。通过将@Async与CompletableFuture结合使用,可以实现更高效的异步任务处理。
接下来,我们将介绍@Async与CompletableFuture结合的使用。
2. 实战案例
首先,需要在 Spring 配置类上添加@EnableAsync注解来启用异步功能:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@Configuration
public class AsyncConfig {
@Bean(name = "asyncExecutor")
public Executor asyncExecutor() {
// 获取当前系统的可用处理器核心数
int core = Runtime.getRuntime().availableProcessors();
// 创建线程池任务执行器实例
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 设置核心线程池大小为系统可用处理器核心数
executor.setCorePoolSize(core);
// 设置最大线程池大小为系统可用处理器核心数
executor.setMaxPoolSize(core);
// 设置队列容量为100
executor.setQueueCapacity(100);
// 设置线程名称前缀为"PackAsync-"
executor.setThreadNamePrefix("PackAsync-");
// 初始化线程池任务执行器
executor.initialize();
// 返回配置好的执行器实例
return executor;
}
}
然后,可以在需要异步执行的方法上添加@Async注解:
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class AsyncService {
@Async
public void doSomethingTimeConsuming() {
// 模拟耗时操作,比如睡眠 3 秒
try {
Thread.sleep(3000);
System.out.println("耗时操作完成,当前线程:" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
在其他地方调用这个异步方法时:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class MyComponent {
@Autowired
private AsyncService asyncService;
public void triggerAsyncTask() {
System.out.println("开始触发异步任务,当前线程:" + Thread.currentThread().getName());
asyncService.doSomethingTimeConsuming();
System.out.println("异步任务已触发,继续执行其他操作,当前线程:" + Thread.currentThread().getName());
}
}
运行MyComponent的triggerAsyncTask方法,会发现主线程不会等待doSomethingTimeConsuming方法执行完成就继续执行后续代码,而异步方法在独立的线程中运行并在大约 3 秒后输出完成信息。
二、CompletableFuture简介与基本使用
CompletableFuture是 Java 8 引入的一个强大的异步编程工具,它提供了丰富的方法来组合、编排异步任务,支持链式调用、异常处理、合并结果等功能,使得异步编程更加灵活和强大。
以下是一个简单的CompletableFuture示例,用于异步计算两个数的和:
import java.util.concurrent.CompletableFuture;
public class CompletableFutureExample {
public static CompletableFuture<Integer> calculateSumAsync(int a, int b) {
return CompletableFuture.supplyAsync(() -> {
// 模拟耗时计算
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return a + b;
});
}
public static void main(String[] args) throws Exception {
CompletableFuture<Integer> sumFuture = calculateSumAsync(5, 10);
sumFuture.thenAccept(result -> System.out.println("计算结果:" + result));
System.out.println("主线程继续执行其他任务...");
// 主线程可以做其他事情,这里为了演示等待结果
sumFuture.get();
}
}
在这个示例中,calculateSumAsync方法使用CompletableFuture.supplyAsync创建一个异步任务,在任务中计算两数之和并返回结果。thenAccept方法用于在异步任务完成后接收结果并进行处理,get方法可以获取异步任务的结果,如果任务未完成则会阻塞当前线程等待结果。
三、@Async与CompletableFuture的优雅结合
在实际应用中,可以将@Async与CompletableFuture结合起来,充分发挥两者的优势。例如,在一个数据处理场景中,需要从多个数据源获取数据并进行合并处理。
假设有两个服务类DataServiceA和DataServiceB,分别从不同数据源获取数据:
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.concurrent.CompletableFuture;
@Service
public class DataServiceA {
@Async
public CompletableFuture<Integer> getDataFromA() {
try {
// 模拟从数据源 A 获取数据的耗时操作
Thread.sleep(3000);
return CompletableFuture.completedFuture(42);
} catch (InterruptedException e) {
e.printStackTrace();
return CompletableFuture.failedFuture(e);
}
}
}
@Service
public class DataServiceB {
@Async
public CompletableFuture<String> getDataFromB() {
try {
// 模拟从数据源 B 获取数据的耗时操作
Thread.sleep(2000);
return CompletableFuture.completedFuture("Hello, Data from B");
} catch (InterruptedException e) {
e.printStackTrace();
return CompletableFuture.failedFuture(e);
}
}
}
然后可以创建一个DataAggregator类来合并数据:
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class DataAggregator {
@Autowired
private DataServiceA dataServiceA;
@Autowired
private DataServiceB dataServiceB;
public void aggregateData() throws ExecutionException, InterruptedException {
CompletableFuture<Integer> dataA = dataServiceA.getDataFromA();
CompletableFuture<String> dataB = dataServiceB.getDataFromB();
CompletableFuture<Void> combinedFuture = CompletableFuture.allOf(dataA, dataB);
combinedFuture.thenRun(() -> {
try {
Integer resultA = dataA.get();
String resultB = dataB.get();
System.out.println("合并数据:" + resultA + " - " + resultB);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
});
}
}
在aggregateData方法中,首先分别调用DataServiceA和DataServiceB的异步方法获取CompletableFuture对象,然后使用CompletableFuture.allOf方法创建一个组合任务,当所有的子任务(dataA和dataB)都完成后,thenRun方法中的代码会被执行,在这里获取并合并数据结果。
通过这种方式,利用@Async将数据获取操作放在独立线程中异步执行,利用CompletableFuture方便地组合和处理异步任务的结果,提高了整个数据处理流程的性能和灵活性。在处理多个异步任务且任务之间存在依赖关系或者需要合并结果时,这种结合方式尤为有效,可以充分利用系统资源,减少线程阻塞和等待时间,提升应用程序的响应速度和吞吐量
四、Spring Boot案例
1. 引入必要的依赖
首先,确保你的Spring项目引入了必要的依赖。对于Maven项目,可以在pom.xml中添加以下依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2. 启用异步支持
在你的Spring Boot应用主类上添加@EnableAsync注解,以启用异步方法执行。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync
public class AsyncApplication {
public static void main(String[] args) {
SpringApplication.run(AsyncApplication.class, args);
}
}
3. 创建一个异步服务
创建一个服务类,并使用@Async注解标记异步方法。这些方法将返回CompletableFuture对象。
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.concurrent.CompletableFuture;
@Service
public class AsyncService {
@Async
public CompletableFuture<String> asyncTask1() {
try {
// 模拟耗时任务
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return CompletableFuture.completedFuture("Task 1 completed");
}
@Async
public CompletableFuture<String> asyncTask2() {
try {
// 模拟耗时任务
Thread.sleep(3000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return CompletableFuture.completedFuture("Task 2 completed");
}
@Async
public CompletableFuture<String> asyncTask3() {
try {
// 模拟耗时任务
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return CompletableFuture.completedFuture("Task 3 completed");
}
}
4. 使用异步服务并处理结果
在控制器或其他服务中调用这些异步方法,并处理返回的CompletableFuture对象。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@RestController
public class AsyncController {
@Autowired
private AsyncService asyncService;
@GetMapping("/execute-async-tasks")
public List<String> executeAsyncTasks() throws InterruptedException, ExecutionException {
CompletableFuture<String> future1 = asyncService.asyncTask1();
CompletableFuture<String> future2 = asyncService.asyncTask2();
CompletableFuture<String> future3 = asyncService.asyncTask3();
// 使用CompletableFuture.allOf等待所有任务完成
CompletableFuture<Void> allFutures = CompletableFuture.allOf(future1, future2, future3);
// 等待所有任务完成并收集结果
return allFutures.thenApply(v -> Stream.of(future1, future2, future3)
.map(CompletableFuture::join)
.collect(Collectors.toList()))
.get();
}
}
5. 运行应用并测试
启动你的Spring Boot应用,并访问/execute-async-tasks端点。你应该会看到所有任务并行执行,并且响应时间为最长任务的时间(在这个例子中是3秒),而不是所有任务时间之和。


5498

被折叠的 条评论
为什么被折叠?



