springboot @Async 多线程使用

本文深入探讨了Spring框架中异步任务的实现机制,包括配置类AsyncConfig的详细解析,异步线程异常处理器AsyncExceptionHandler的使用,以及异步方法的具体示例。同时,文章还分析了多线程的执行过程,并提供了实际应用场景,如如何避免服务端处理大量数据时的超时问题。

 

测试版本

 

1.5 和 2.0.4 完全一样

 

 

一、配置类 AsyncConfig

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 org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import com.ly.async.AsyncExceptionHandler;
import java.util.concurrent.Executor;

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
    
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(100);
        executor.setQueueCapacity(100);
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60 * 10);
        executor.setThreadNamePrefix("AsyncThread-");
        executor.initialize(); //如果不初始化,导致找到不到执行器
        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new AsyncExceptionHandler();
    }
}
View Code

配置线程池的初始化属性

 

二、异步线程异常处理器 

AsyncExceptionHandler

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import java.lang.reflect.Method;
import java.util.Arrays;

public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler{
     
    private static final Logger log = LoggerFactory.getLogger(AsyncExceptionHandler.class);
        @Override
        public void handleUncaughtException(Throwable ex, Method method, Object... params) {
            log.error("Async method has uncaught exception, params:{}" + Arrays.toString(params));

            if (ex instanceof AsyncException) {
                AsyncException asyncException = (AsyncException) ex;
                log.error("asyncException:"  + asyncException.getMessage());
            }

            log.error("Exception :", ex);
        }
}
View Code

没有返回值的线程,如果跑出异常,会被处理,有返回值的异常信息可以通过 get() 方法获取

 通过Future的get方法捕获异常

 

三、异步方法

@Async
    public void dealNoReturnTask() {
        logger.info("返回值为void的异步调用开始" + Thread.currentThread().getName());
        //int a =1/0;
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        logger.info("返回值为void的异步调用结束" + Thread.currentThread().getName());
    }

    @Async
    public Future<String> dealHaveReturnTask(int i) {
        //int a =1/0;
        logger.info("asyncInvokeReturnFuture, parementer=" + i);
        Future<String> future;
        try {
            Thread.sleep(1000 * i);
            future = new AsyncResult<String>("success:" + i);
        } catch (InterruptedException e) {
            future = new AsyncResult<String>("error");
        }
        return future;
    }

 

分别是有返回值和没有返回值的

 

四、测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class ApplicationTests {

    private static final Logger log = LoggerFactory.getLogger(ApplicationTests.class);
   
    @Autowired
    private AsyncTask asyncTask;

@Test
    public void testAsync2() throws InterruptedException, ExecutionException {
        Future<String> result = asyncTask.dealHaveReturnTask(2);
        System.out.println("输出" + result.get());
    }
View Code

返回success:2

 

五、理解多线程

 @Test
    public void testAsync() throws InterruptedException, ExecutionException {
        System.out.println("线程名称" + Thread.currentThread().getName());
        System.out.println("开始S1");
        asyncTask.sum();
        System.out.println("S2 调用完sum()");
    }
@Async
    public void sum() {
        System.out.println("进入sum");
        
        int sum = 0;
        for(int i = 0; i < 1000; i++) {
            
            sum += i;
        }
        System.out.println("线程名称" + Thread.currentThread().getName());
        System.out.println(" sum " + sum);
    }

 

 

执行到sum方法的时候,主线程并没有停止,而是继续,子线程去执行里面的方法

 

 六、使用场景

1、场景

 

  最近做项目的时候遇到了一个小问题:从前台提交到服务端A,A调用服务端B处理超时,原因是前端一次请求往db插1万数据,插完之后会去清理缓存、发送消息。

服务端的有三个操作 a、插DB b、清理cache  c、发送消息。1万条数据,说多不多,说少不少.况且不是单单insert。出现超时现象,不足为奇了吧~~

 

2、分析

 

  如何避免超时呢?一次请求处理辣么多数据,可分多次请求处理,每次请求处理100条数据,可以有效解决超时问题. 为了不影响用户的体验,请求改为ajax 异步请求。

除此之外,仔细分析下场景. a 操作是本次处理的核心. 而b、c操作可以异步处理。换句话说,a操作处理完可以马上返回给结果, 不必等b、c处理完再返回。b、c操作可以放在一个异步任务去处理。

-------------

--举个简单的例子:
假设有个请求,这个请求服务端的处理需要执行3个很缓慢的IO操作(比如数据库查询或文件查询),那么正常的顺序可能是(括号里面代表执行时间):
a、读取文件1  (10ms)
b、处理1的数据(1ms)
c、读取文件2  (10ms)
d、处理2的数据(1ms)
e、读取文件3  (10ms)
f、处理3的数据(1ms)
g、整合1、2、3的数据结果 (1ms)
单线程总共就需要34ms。
那如果你在这个请求内,把ab、cd、ef分别分给3个线程去做,就只需要12ms了。

所以多线程不是没怎么用,而是,你平常要善于发现一些可优化的点。然后评估方案是否应该使用。
假设还是上面那个相同的问题:但是每个步骤的执行时间不一样了。
a、读取文件1  (1ms)
b、处理1的数据(1ms)
c、读取文件2  (1ms)
d、处理2的数据(1ms)
e、读取文件3  (28ms)
f、处理3的数据(1ms)
g、整合1、2、3的数据结果 (1ms)
单线程总共就需要34ms。
如果还是按上面的划分方案(上面方案和木桶原理一样,耗时取决于最慢的那个线程的执行速度),在这个例子中是第三个线程,执行29ms。那么最后这个请求耗时是30ms。比起不用单线程,就节省了4ms。但是有可能线程调度切换也要花费个1、2ms。因此,这个方案显得优势就不明显了,还带来程序复杂度提升。不太值得。

那么现在优化的点,就不是第一个例子那样的任务分割多线程完成。而是优化文件3的读取速度。
可能是采用缓存和减少一些重复读取。
首先,假设有一种情况,所有用户都请求这个请求,那其实相当于所有用户都需要读取文件3。那你想想,100个人进行了这个请求,相当于你花在读取这个文件上的时间就是28×100=2800ms了。那么,如果你把文件缓存起来,那只要第一个用户的请求读取了,第二个用户不需要读取了,从内存取是很快速的,可能1ms都不到。

参考:

https://www.cnblogs.com/chenmo-xpw/p/5652029.html

 

项目

 

转载于:https://www.cnblogs.com/lyon91/p/10097859.html

<think>好的,我现在需要帮助用户了解Spring Boot中@Async注解的使用方法及示例。根据用户提供的引用内容,我需要整理相关信息并确保回答结构清晰,同时遵循系统级指令中的格式要求。首先,我需要确认用户的需求:他们可能想知道如何正确使用@Async注解,如何启用异步支持,以及可能遇到的一些问题,比如线程池配置或异常处理。 根据引用[3],启用异步支持需要在启动类或配置类添加@EnableAsync。这一点很重要,必须首先提到。然后,引用[1]中的示例代码展示了在Spring Boot应用程序中使用@Async的基本用法,比如在方法上添加注解。用户可能还需要了解如何定义异步方法,以及如何调用它们。例如,在示例中,sayHi方法被标记为@Async,并通过ApplicationRunner调用。 接下来,用户可能关心线程池的配置。引用[4]提到线程池实例的详解,因此需要说明如何自定义线程池。例如,通过@Bean创建一个TaskExecutor,并设置线程池的参数,如核心线程数、最大线程数等。同时,引用[5]介绍CompletableFuture,这可能涉及到异步方法返回结果的处理,需要解释如何返回Future类型。 另外,需要注意常见问题,比如异步方法不能在同一类中直接调用,否则注解不会生效。这一点在引用[1]的示例中也有所体现,他们通过注入自身实例来调用异步方法。此外,异常处理也是一个关键点,需要提到使用AsyncConfigurer或捕获Future异常的方法。 最后,用户可能想了解@Async的应用场景,比如处理耗时任务或发送邮件等。需要结合引用中的信息,给出实际应用的例子,并确保回答中引用了正确的来源。同时,生成相关问题时要围绕这些知识点,比如配置线程池、解决不生效的问题、异常处理等。 在组织回答时,需要按照步骤逐步说明,从启用异步支持、基本使用、线程池配置到常见问题,每个部分都引用对应的参考文献。同时,代码示例要符合格式要求,使用正确的LaTeX语法,行内公式用$...$,独立公式用$$...$$,但这里可能代码块较多,需要注意代码的格式是否正确。 现在需要检查是否有遗漏的信息,比如是否提到了CompletableFuture的使用,以及是否涵盖了所有用户可能遇到的问题。确保每个部分都有对应的引用标注,并且语言流畅自然,符合中文表达习惯。</think>### Spring Boot @Async 注解使用方法及示例 #### 一、启用异步支持 在 Spring Boot 中,**必须显式启用异步功能**。需在启动类或配置类添加注解 `@EnableAsync`: ```java @SpringBootApplication @EnableAsync public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` [^3] #### 二、基本使用方法 1. **定义异步方法** 在需要异步执行的方法上添加 `@Async` 注解: ```java @Service public class AsyncService { @Async public void sendNotification(String message) { System.out.println(Thread.currentThread().getName() + " 发送消息: " + message); } } ``` [^2] 2. **调用异步方法** 通过注入服务类实例调用异步方法(**不可在同类中直接调用**): ```java @RestController public class DemoController { @Autowired private AsyncService asyncService; @GetMapping("/send") public String triggerAsync() { asyncService.sendNotification("订单已创建"); return "操作已触发"; } } ``` #### 三、线程池配置(核心优化) 默认使用 `SimpleAsyncTaskExecutor`(非线程池),**建议自定义线程池**: ```java @Configuration public class AsyncConfig { @Bean("customTaskExecutor") public TaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(100); executor.setThreadNamePrefix("Async-"); executor.initialize(); return executor; } } ``` 使用时指定线程池: ```java @Async("customTaskExecutor") public void processData() { /*...*/ } ``` [^4] #### 四、返回值处理 异步方法可返回 `CompletableFuture` 实现结果回调: ```java @Async public CompletableFuture<String> fetchData() { return CompletableFuture.completedFuture("数据加载完成"); } // 调用示例 CompletableFuture<String> future = asyncService.fetchData(); future.thenAccept(result -> System.out.println(result)); ``` [^5] #### 五、常见问题解决 1. **注解不生效** - 未添加 `@EnableAsync` - 同类中直接调用异步方法(需通过代理对象调用) - 异步方法未声明为 `public` 2. **异常处理** - 实现 `AsyncUncaughtExceptionHandler`: ```java @Configuration public class AsyncExceptionConfig implements AsyncConfigurer { @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return (ex, method, params) -> System.err.println("异步方法异常: " + method.getName() + ex.getMessage()); } } ``` - 或捕获 `Future` 异常: ```java try { future.get(); } catch (InterruptedException | ExecutionException e) { // 处理异常 } ``` #### 六、典型应用场景 1. 发送短信/邮件通知 2. 日志异步写入 3. 大数据批量处理 4. 第三方API调用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值