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 密集型、需要事务的场景别乱用。

相关推荐
他是龙5511 分钟前
71:Python安全 & 反序列化 & PYC反编译 & 格式化字符串安全
开发语言·python·安全
wjs202428 分钟前
Go 语言接口
开发语言
likerhood34 分钟前
Fastjson中的JSON.parseObject()详细讲解
java·json
草莓熊Lotso37 分钟前
《告别 “会用不会讲”:C++ string 底层原理拆解 + 手撕实现,面试 / 开发都适用》
开发语言·c++·面试
KNeeg_37 分钟前
黑马点评完整代码(RabbitMQ优化)+简历编写+面试重点 ⭐
java·redis·后端·spring·面试·职场和发展·黑马点评
水木流年追梦39 分钟前
【python因果库实战27】逆概率加权模型2
开发语言·python
张槊哲1 小时前
C++ 进阶指南:如何丝滑地理解与实践多线程与多进程
开发语言·c++·算法
铁皮哥1 小时前
【后端/Agent 开发】给你的项目配置一套 .claude/ 工作流:别再裸用 Claude Code 了!
java·windows·python·spring·github·maven·生活
雪度娃娃1 小时前
Effective Modern C++——型别推导
开发语言·c++
乐之者v1 小时前
AI编程 -- codex添加代码,在intellij Idea中没有显示,如何处理?
java·ide·intellij-idea