SpringBoot 异步处理与线程池实战

先问一个问题:你的项目里,有没有类似这样的代码?

java 复制代码
new Thread(() -> {
    // 发送邮件、记录日志、调用外部接口
}).start();

new Thread() 的问题很明显:每次请求都创建新线程,用完就丢,线程数量无上限,分分钟把系统搞崩。

正确的做法是使用线程池。SpringBoot 内置了强大的异步支持,几行配置就能搞定。

一、快速上手

1.1 开启异步

在启动类或配置类上加一个注解:

java 复制代码
@SpringBootApplication
@EnableAsync   // 就这一行
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

1.2 写一个异步方法

java 复制代码
@Service
@Slf4j
public class NotificationService {
    
    @Async   // 加了这个注解,方法就变成异步了
    public void sendEmail(String to, String content) {
        log.info("发送邮件到: {}", to);
        // 耗时操作...
    }
}

1.3 调用

java 复制代码
@RestController
public class UserController {
    
    @Autowired
    private NotificationService notificationService;
    
    @PostMapping("/register")
    public String register(String username, String email) {
        // 主流程立即返回,邮件在后台慢慢发
        notificationService.sendEmail(email, "欢迎注册");
        return "注册成功";
    }
}

效果:调用方不会等待邮件发送完成,接口会立即返回。

二、三个必知的坑

现象 解决方案
内部调用不生效 同一个类的方法调用 @Async 方法,还是同步执行 把异步方法放到单独的 Service 中
private 不生效 异步方法写成 private 必须是 public
返回普通对象会失效 返回 User、String 等 只能返回 void 或 CompletableFuture

三、自定义线程池(生产必做)

默认的线程池太简陋,生产环境一定要自己配置:

java 复制代码
@Configuration
@EnableAsync
public class AsyncConfig {
    
    @Bean(name = "taskExecutor")
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);        // 核心线程数
        executor.setMaxPoolSize(20);        // 最大线程数
        executor.setQueueCapacity(100);     // 队列大小
        executor.setThreadNamePrefix("async-"); // 线程名前缀
        executor.setRejectedExecutionHandler(
            new ThreadPoolExecutor.CallerRunsPolicy() // 满了就让调用方执行
        );
        executor.initialize();
        return executor;
    }
}

参数怎么设?

场景 corePoolSize maxPoolSize queueCapacity
轻量任务多 10-20 50-100 200-500
重量任务多 2-5 10-20 100-200
IO 密集型 CPU核数×2 CPU核数×4 200左右

使用自定义线程池:

java 复制代码
@Async("taskExecutor")  // 指定线程池名称
public void sendEmail(String to, String content) {
    // ...
}

四、处理异步任务异常

异步方法抛出的异常,调用方是抓不到的。需要自定义异常处理器:

java 复制代码
@Configuration
public class AsyncExceptionConfig implements AsyncConfigurer {
    
    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return (ex, method, params) -> {
            log.error("异步方法执行失败: {}.{}", 
                method.getDeclaringClass().getName(), 
                method.getName(), ex);
            // 可以在这里发送告警
        };
    }
}

五、有返回值的异步

如果需要拿到异步执行的结果:

java 复制代码
@Async
public CompletableFuture<String> fetchData() {
    // 模拟耗时
    Thread.sleep(3000);
    return CompletableFuture.completedFuture("结果数据");
}

// 调用方
CompletableFuture<String> future = dataService.fetchData();
String result = future.get(5, TimeUnit.SECONDS);  // 等待最多5秒

总结

要点 一句话
开启 @EnableAsync
使用 @Async
线程池 必须自定义,别用默认的
异常 单独处理,别丢了
返回值 void 或 CompletableFuture

最后记住一句话:

异步不是银弹。IO 密集型、不需要返回结果的场景用它;CPU 密集型、需要事务的场景别乱用。

相关推荐
Hui Baby15 分钟前
Spring Security
java·后端·spring
IT笔记1 小时前
【Rust】Rust Match 模式匹配详解
java·开发语言·rust
逝水无殇1 小时前
C# 运算符重载详解
开发语言·后端·c#
我才是银古1 小时前
构建 Java GIS 工具库:从碎片化 API 到统一抽象的设计实践
java·gis·geotools·gdal·esri
TPBoreas2 小时前
配置信息防泄露方案:.env 环境隔离详解(dotenv-java)
java·开发语言
敲代码的嘎仔2 小时前
实习日志day6--实习日志day6--title命名规范化&businessType纠正&补充缺失的@Log注解&报警与通信模块补充&产出阶段总结文档
java·开发语言·人工智能·git·python·实习·大二
江华森2 小时前
Python 实现高德地图找房(一):环境搭建与数据爬虫
开发语言·爬虫·python
杜子不疼.2 小时前
【Qt初识】信号槽(三):机制意义、断开连接与 Lambda 表达式
开发语言·数据库·qt
编程(变成)小辣鸡2 小时前
Spring事务失效场景
java·后端·spring
极客先躯3 小时前
高级java每日一道面试题-2026年03月30日-实战篇[Docker]-如何监控容器的网络流量?
java·运维·docker·容器·prometheus·高级面试