文章目录
- 一、异步方法的使用
- 1. 开启异步支持
- 2. 定义异步方法
- 3. 调用异步方法
- 踩坑记录
- 心得体会
- 二、线程池配置
- 1. 自定义线程池
- 2. 使用自定义线程池
- 踩坑记录
- 心得体会
- 三、异步任务的监控与管理
- 1. 日志记录
- 2. 异常处理
- 3. 线程池监控
- 踩坑记录
- 心得体会
在现代应用程序开发中,异步编程是提升系统性能和响应能力的重要手段。Spring Boot 提供了便捷的方式来实现异步编程,下面将详细介绍异步方法的使用、线程池配置以及异步任务的监控与管理。
一、异步方法的使用
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 AsyncApp {
public static void main(String[] args) {
SpringApplication.run(AsyncApp.class, args);
}
}
2. 定义异步方法
在需要异步执行的方法上添加 @Async
注解。
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.concurrent.CompletableFuture;
@Service
public class AsyncService {
@Async
public CompletableFuture<String> asyncTask() {
try {
// 模拟耗时操作
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return CompletableFuture.completedFuture("异步任务执行完成");
}
}
3. 调用异步方法
在控制器或者其他服务类中注入包含异步方法的服务类,并调用异步方法。
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;
@RestController
public class AsyncController {
@Autowired
private AsyncService asyncService;
@GetMapping("/async")
public String asyncCall() throws InterruptedException, ExecutionException {
CompletableFuture<String> future = asyncService.asyncTask();
// 可以在此处执行其他操作
// 等待异步任务完成并获取结果
return future.get();
}
}
踩坑记录
- 注解未生效:若
@EnableAsync
未添加到主应用类,或者异步方法所在类未被 Spring 管理(如未加@Service
等注解),@Async
注解将不生效。此外,若在同一类中直接调用异步方法,而非通过 Spring 代理对象调用,也不会异步执行,因为 Spring 的 AOP 代理机制基于代理对象。 - 返回值处理不当:有返回值的异步方法需用
CompletableFuture
包装,若直接返回普通类型,调用get()
方法获取结果时可能阻塞或抛异常。
心得体会
要深刻理解 Spring 的 AOP 代理机制对 @Async
注解的影响,设计代码时尽量通过依赖注入和代理对象调用异步方法。同时,对于有返回值的异步方法,务必使用 CompletableFuture
包装,并妥善处理 get()
方法可能抛出的异常。
二、线程池配置
1. 自定义线程池
默认情况下,Spring Boot 使用简单线程池执行异步任务。可通过创建配置类,使用 ThreadPoolTaskExecutor
自定义线程池。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean(name = "customAsyncExecutor")
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程数
executor.setCorePoolSize(5);
// 最大线程数
executor.setMaxPoolSize(10);
// 队列容量
executor.setQueueCapacity(25);
// 线程名前缀
executor.setThreadNamePrefix("CustomAsyncThread-");
// 线程池关闭时等待所有任务完成
executor.setWaitForTasksToCompleteOnShutdown(true);
// 等待任务完成的时间
executor.setAwaitTerminationSeconds(60);
// 初始化线程池
executor.initialize();
return executor;
}
}
2. 使用自定义线程池
在 @Async
注解中指定使用自定义线程池的名称。
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.concurrent.CompletableFuture;
@Service
public class AsyncService {
@Async("customAsyncExecutor")
public CompletableFuture<String> asyncTask() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return CompletableFuture.completedFuture("异步任务执行完成");
}
}
踩坑记录
- 参数设置不合理:核心线程数、最大线程数和队列容量设置需根据业务场景调整。核心线程数过小,任务处理不及时;最大线程数过大,占用过多系统资源。队列容量过小,新任务可能被拒绝;过大则可能导致任务堆积,影响系统响应时间。
- 未正确初始化:配置线程池时,若忘记调用
executor.initialize()
方法,线程池可能无法正常工作,导致异步任务无法执行或执行过程中出现异常。
心得体会
配置线程池时,要充分考虑业务特点和系统资源情况。对于 I/O 密集型任务,可适当增大核心线程数;对于 CPU 密集型任务,核心线程数可相对较小。同时,务必确保调用 initialize()
方法初始化线程池。
三、异步任务的监控与管理
1. 日志记录
在异步方法中添加日志记录,跟踪任务执行过程。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.concurrent.CompletableFuture;
@Service
public class AsyncService {
private static final Logger logger = LoggerFactory.getLogger(AsyncService.class);
@Async("customAsyncExecutor")
public CompletableFuture<String> asyncTask() {
logger.info("异步任务开始执行");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
logger.error("异步任务被中断", e);
Thread.currentThread().interrupt();
}
logger.info("异步任务执行完成");
return CompletableFuture.completedFuture("异步任务执行完成");
}
}
2. 异常处理
实现 AsyncUncaughtExceptionHandler
接口处理异步方法中抛出的未捕获异常。
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Component
public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
@Override
public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
System.err.println("异步方法 " + method.getName() + " 抛出未捕获异常");
for (Object param : objects) {
System.err.println("参数: " + param);
}
throwable.printStackTrace();
}
}
在配置类中配置异常处理器:
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
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 {
@Bean(name = "customAsyncExecutor")
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(25);
executor.setThreadNamePrefix("CustomAsyncThread-");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(60);
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new CustomAsyncExceptionHandler();
}
}
3. 线程池监控
通过 ThreadPoolTaskExecutor
提供的方法获取线程池状态信息。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
@Service
public class ThreadPoolMonitorService {
@Autowired
private ThreadPoolTaskExecutor customAsyncExecutor;
public int getActiveThreadCount() {
return customAsyncExecutor.getActiveCount();
}
public int getQueueSize() {
return customAsyncExecutor.getThreadPoolExecutor().getQueue().size();
}
}
踩坑记录
- 日志记录不详细:若日志信息不够详细,排查问题时会遇到困难。例如,只记录任务开始和结束信息,未记录关键步骤执行情况和异常信息,难以定位问题。
- 异常处理不全面:实现
AsyncUncaughtExceptionHandler
接口时,若未全面处理异常,可能导致部分异常信息丢失。
心得体会
在异步方法中添加详细日志记录,包括任务开始、关键步骤、异常信息等,以便出现问题时能快速定位。实现异常处理器时,要尽可能全面记录异常信息,为问题排查和修复提供有力支持。
总之,Spring Boot 异步编程能显著提升系统性能和响应能力,但实际使用中需注意各种细节,避免踩坑。通过不断实践和总结经验,才能更好地掌握异步编程技巧。