异步编程优化:@Async与CompletableFuture的优雅结合

一、@Async简介与基本使用

1. 简介

@AsyncCompletableFuture 是实现异步处理的强大工具组合。@Async 是Spring框架提供的一个注解,用于标记方法以表明它将在Spring管理的线程池中的另一个线程上异步执行。这使得开发人员能够在不阻塞主线程的情况下执行耗时的任务,从而提高应用程序的整体性能和响应速度。

CompletableFuture 是Java 8引入的一个强大的类,它代表了一个可能尚未完成的计算的结果。CompletableFuture 提供了丰富的API来支持异步编程模式,如回调、组合操作、错误处理等。通过将@AsyncCompletableFuture结合使用,可以实现更高效的异步任务处理。

接下来,我们将介绍@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());
    }
}

运行MyComponenttriggerAsyncTask方法,会发现主线程不会等待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方法可以获取异步任务的结果,如果任务未完成则会阻塞当前线程等待结果。

三、@AsyncCompletableFuture的优雅结合

在实际应用中,可以将@AsyncCompletableFuture结合起来,充分发挥两者的优势。例如,在一个数据处理场景中,需要从多个数据源获取数据并进行合并处理。

假设有两个服务类DataServiceADataServiceB,分别从不同数据源获取数据:

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方法中,首先分别调用DataServiceADataServiceB的异步方法获取CompletableFuture对象,然后使用CompletableFuture.allOf方法创建一个组合任务,当所有的子任务(dataAdataB)都完成后,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秒),而不是所有任务时间之和。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yaml墨韵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值