跳转至

Spring Boot Java 11 中使函数异步执行

简介

在 Spring Boot 应用开发中,使用 Java 11 进行编程时,有时会遇到一些耗时的操作,如网络请求、文件读写等。如果这些操作在主线程中同步执行,会阻塞主线程,导致应用的响应性能下降。为了避免这种情况,我们可以将这些耗时操作封装成异步函数,让它们在单独的线程中执行,从而提高应用的并发处理能力和响应速度。本文将详细介绍在 Spring Boot Java 11 中使函数异步执行的基础概念、使用方法、常见实践以及最佳实践。

目录

  1. 基础概念
  2. 使用方法
  3. 常见实践
  4. 最佳实践
  5. 小结
  6. 参考资料

基础概念

同步和异步

  • 同步执行:在同步执行模式下,程序按照代码的顺序依次执行,当遇到一个耗时操作时,主线程会被阻塞,直到该操作完成后才会继续执行后续代码。
  • 异步执行:在异步执行模式下,当遇到一个耗时操作时,主线程不会被阻塞,而是继续执行后续代码,耗时操作会在另一个线程中独立执行,执行完成后可以通过回调或其他方式通知主线程。

Spring 中的异步支持

Spring 框架提供了对异步方法调用的支持,通过 @Async 注解可以将一个方法标记为异步方法。当调用该方法时,Spring 会将该方法的执行交给一个线程池中的线程来处理,而不是在调用线程中执行。

使用方法

步骤 1:启用异步支持

在 Spring Boot 应用的主类上添加 @EnableAsync 注解,以启用 Spring 的异步方法调用功能。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@EnableAsync
public class MyApp {
    public static void main(String[] args) {
        SpringApplication.run(MyApp.class, args);
    }
}

步骤 2:创建异步方法

在需要异步执行的方法上添加 @Async 注解。注意,该方法必须是公共方法,并且所在的类必须是 Spring 管理的 Bean。

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.concurrent.CompletableFuture;

@Service
public class MyService {
    @Async
    public CompletableFuture<String> asyncMethod() {
        try {
            // 模拟耗时操作
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return CompletableFuture.completedFuture("Async method completed");
    }
}

步骤 3:调用异步方法

在其他 Spring Bean 中注入 MyService 并调用异步方法。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.util.concurrent.CompletableFuture;

@Component
public class MyRunner implements CommandLineRunner {
    @Autowired
    private MyService myService;

    @Override
    public void run(String... args) throws Exception {
        CompletableFuture<String> future = myService.asyncMethod();
        // 主线程可以继续执行其他任务
        System.out.println("Main thread continues...");
        // 获取异步方法的结果
        String result = future.get();
        System.out.println("Async method result: " + result);
    }
}

常见实践

处理多个异步任务

可以同时发起多个异步任务,并使用 CompletableFuture 的组合方法来处理它们的结果。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.util.concurrent.CompletableFuture;

@Component
public class MultipleAsyncTasksRunner implements CommandLineRunner {
    @Autowired
    private MyService myService;

    @Override
    public void run(String... args) throws Exception {
        CompletableFuture<String> future1 = myService.asyncMethod();
        CompletableFuture<String> future2 = myService.asyncMethod();

        // 等待所有任务完成
        CompletableFuture<Void> allFutures = CompletableFuture.allOf(future1, future2);
        CompletableFuture<String> combinedFuture = allFutures.thenApply(v -> {
            try {
                return future1.get() + " and " + future2.get();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });

        String result = combinedFuture.get();
        System.out.println("Combined result: " + result);
    }
}

异常处理

在异步方法中,如果发生异常,可以使用 CompletableFutureexceptionally 方法来处理。

@Service
public class MyService {
    @Async
    public CompletableFuture<String> asyncMethodWithException() {
        try {
            throw new RuntimeException("Something went wrong");
        } catch (Exception e) {
            return CompletableFuture.failedFuture(e);
        }
    }
}

@Component
public class ExceptionHandlingRunner implements CommandLineRunner {
    @Autowired
    private MyService myService;

    @Override
    public void run(String... args) throws Exception {
        CompletableFuture<String> future = myService.asyncMethodWithException();
        String result = future.exceptionally(ex -> "Error: " + ex.getMessage()).get();
        System.out.println(result);
    }
}

最佳实践

配置线程池

默认情况下,Spring 使用一个简单的线程池来执行异步方法。为了更好地控制异步任务的执行,可以自定义线程池。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
    @Override
    @Bean(name = "asyncExecutor")
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(25);
        executor.setThreadNamePrefix("AsyncThread-");
        executor.initialize();
        return executor;
    }
}

然后在 @Async 注解中指定线程池的名称:

@Service
public class MyService {
    @Async("asyncExecutor")
    public CompletableFuture<String> asyncMethod() {
        // ...
    }
}

避免在同一个类中调用异步方法

如果在同一个类中调用异步方法,@Async 注解将不会生效,因为 Spring 的 AOP 代理机制是基于类的代理,同一个类中的方法调用不会经过代理。因此,异步方法应该在不同的 Bean 中调用。

小结

在 Spring Boot Java 11 中,通过 @Async 注解可以很方便地将方法标记为异步方法,从而提高应用的并发处理能力和响应速度。同时,使用 CompletableFuture 可以更好地处理异步方法的结果和异常。在实际开发中,需要注意配置线程池和避免在同一个类中调用异步方法等最佳实践。

参考资料