让你的 Spring Boot 应用飞起来!@Async 异步编程魔法
2025-02-15 08:44 阅读(58)

关注公众号:程序员老左   发现更多新技术

这篇文章是用来详细讲解 @Async 注解,它解决的问题,以及使用方法,并提供由易到难的实例,最后用大白话总结,方便各位理解。

一、@Async 是什么?解决了什么问题?

https://www.zuocode.com

@Async 是什么: @Async 是 Spring 框架提供的一个注解,用于将方法标记为异步执行。简单来说,就是让这个方法在另一个线程中执行,而不是在调用它的线程中执行。


解决了什么问题:

提高响应速度: 当一个方法执行时间较长时,如果同步执行,会导致调用它的线程阻塞,影响程序的响应速度。使用 @Async 可以将这个方法放到另一个线程中执行,让调用线程可以继续执行其他任务,从而提高程序的响应速度。

提高并发能力: 当需要同时执行多个耗时任务时,可以使用 @Async 将这些任务放到不同的线程中执行,从而提高程序的并发能力。

避免阻塞主线程: 在 Web 应用中,如果某个请求处理需要执行耗时操作,可以使用 @Async 将这个操作放到另一个线程中执行,避免阻塞主线程,从而提高 Web 应用的吞吐量。


二、@Async 的使用方法

开启异步支持: 在 Spring Boot 应用的主类上添加 @EnableAsync 注解,开启异步支持。

标记异步方法: 在需要异步执行的方法上添加 @Async 注解。


三、@Async 实例 (由易到难)

1. 简单示例:异步发送邮件

需求: 用户注册成功后,异步发送一封欢迎邮件。

步骤:

创建 Spring Boot 应用:

使用 Spring Initializr 创建一个 Spring Boot 应用。

添加 spring-boot-starter-web 依赖。


开启异步支持:

在 MyApplication.java (或者你的主类) 中添加 @EnableAsync 注解:

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

@SpringBootApplication
@EnableAsync // 开启异步支持
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

创建邮件发送服务:

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

@Service
public class EmailService {

    @Async // 标记为异步方法
    public void sendWelcomeEmail(String email) {
        // 模拟发送邮件,耗时 5 秒
        System.out.println("Sending welcome email to " + email + "...");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Welcome email sent to " + email);
    }
}

解释:

@Async:将 sendWelcomeEmail() 方法标记为异步方法。

Thread.sleep(5000):模拟发送邮件的耗时操作。

创建用户注册服务:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Autowired
    private EmailService emailService;

    public void registerUser(String email) {
        // 模拟用户注册
        System.out.println("Registering user with email: " + email);

        // 异步发送欢迎邮件
        emailService.sendWelcomeEmail(email);

        System.out.println("User registered successfully.");
    }
}

解释:

emailService.sendWelcomeEmail(email):调用 EmailService 的 sendWelcomeEmail() 方法发送邮件。

创建 Controller:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/register")
    public String register(@RequestParam("email") String email) {
        userService.registerUser(email);
        return "User registration initiated.  Check console for async email sending.";
    }
}

运行应用:

运行 MyApplication 类。


测试 API:

在浏览器或使用 curl 命令访问以下 URL:

bash 代码解读复制代码http://localhost:8080/register?email=test@example.com

你应该会看到 "User registration initiated." 的响应,并且在控制台中会看到异步发送邮件的日志。

// MyApplication.java
package com.example;

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

@SpringBootApplication
@EnableAsync
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

// EmailService.java
package com.example;

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

@Service
public class EmailService {

    @Async
    public void sendWelcomeEmail(String email) {
        System.out.println("Sending welcome email to " + email + "...");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Welcome email sent to " + email);
    }
}

// UserService.java
package com.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Autowired
    private EmailService emailService;

    public void registerUser(String email) {
        System.out.println("Registering user with email: " + email);
        emailService.sendWelcomeEmail(email);
        System.out.println("User registered successfully.");
    }
}

// UserController.java
package com.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/register")
    public String register(@RequestParam("email") String email) {
        userService.registerUser(email);
        return "User registration initiated.  Check console for async email sending.";
    }
}

2. 中级示例:自定义线程池

需求: 使用自定义的线程池来执行异步任务。

步骤:

创建线程池配置类:

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 = "taskExecutor") // 指定线程池的名称
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5); // 核心线程数
        executor.setMaxPoolSize(10); // 最大线程数
        executor.setQueueCapacity(25); // 队列容量
        executor.setThreadNamePrefix("MyAsyncThread-"); // 线程名称前缀
        executor.initialize();
        return executor;
    }
}

解释:

@Configuration:将 AsyncConfig 类标记为一个配置类。

@EnableAsync:开启异步支持。

@Bean(name = "taskExecutor"):创建一个名为 taskExecutor 的 Bean,类型为 Executor。

ThreadPoolTaskExecutor:Spring 提供的线程池实现。

setCorePoolSize():设置核心线程数。

setMaxPoolSize():设置最大线程数。

setQueueCapacity():设置队列容量。

setThreadNamePrefix():设置线程名称前缀。


指定使用的线程池:

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

@Service
public class EmailService {

    @Async("taskExecutor") // 指定使用的线程池名称
    public void sendWelcomeEmail(String email) {
        System.out.println("Sending welcome email to " + email + "...");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Welcome email sent to " + email);
    }
}

解释:

@Async("taskExecutor"):指定使用名为 taskExecutor 的线程池来执行 sendWelcomeEmail() 方法。


3. 高级示例:异步任务的异常处理

需求: 捕获异步任务中发生的异常,并进行处理。


步骤:

实现 AsyncConfigurer 接口:

import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;

import java.lang.reflect.Method;
import java.util.concurrent.Executor;

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        // 可以自定义线程池
        return null;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new MyAsyncExceptionHandler();
    }
}

解释:

AsyncConfigurer:Spring 提供的接口,用于配置异步任务。

getAsyncExecutor():可以自定义线程池。

getAsyncUncaughtExceptionHandler():用于指定异步任务的异常处理器。


创建异常处理器:

java 代码解读复制代码

import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;

import java.lang.reflect.Method;

public class MyAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {

    @Override
    public void handleUncaughtException(Throwable ex, Method method, Object... params) {
        System.err.println("Async method has uncaught exception:");
        System.err.println("Method: " + method.getName());
        System.err.println("Exception message: " + ex.getMessage());
    }
}

解释:

AsyncUncaughtExceptionHandler:Spring 提供的接口,用于处理异步任务中发生的未捕获异常。

handleUncaughtException():处理异常的方法。



四、大白话总结

@Async: 就像请了一个 "临时工",帮你干一些比较慢的活,这样你就不用一直等着,可以先去干其他的事情。

@EnableAsync: 告诉 Spring "我要用临时工了!"

线程池: 就像一个 "临时工管理中心",可以管理多个 "临时工",让他们更有效率地工作。

异常处理: 就像给 "临时工" 买了保险,万一他们出了什么问题,也能及时处理。


更简洁的总结:

@Async 让方法异步执行,就像请了个临时工。

@EnableAsync 开启异步支持,告诉 Spring 可以用临时工了。

可以自定义线程池,管理临时工。

可以进行异常处理,给临时工买保险。


希望这篇文章能够帮助你理解 @Async 注解!


作者:流_云

链接:https://juejin.cn