@Async注解实现异步调用

本文详细介绍了在Spring Boot中使用@Async注解实现异步调用的方法,包括异步调用的事务处理机制、失效情况及处理办法。强调了在大量请求下配置线程池的重要性,以及@Async注解不能与@Controller、@Service等同时使用。并提供了配置自定义线程池和处理异步方法异常的示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

@Async默认异步配置使用的是SimpleAsyncTaskExecutor,该线程池默认来一个任务创建一个线程,在大量的请求的时候,这时就会不断创建大量线程,极有可能压爆服务器内存

@Async的时候一定要设置线程数,以防万一OOM

​ 异步调用,类似我们多年前的ajax调用,局部刷新,整体不变,当然,在java的后台的异步调用,类似于自己实现一个多线程的程序,任务开启一个线程后由它最去执行,我们其实是不能干预太多的。。

​ 在实际的开发中,如果某一个方法需要异步去执行,那么我们可以在它前面加上注解。@Async

1、 @Async介绍

​ 在Spring中,基于@Async标注的方法,称之为异步方法;这些方法将在执行的时候,将会在独立的线程中被执行,调用者无需等待它的完成,即可继续其他的操作。分为不带参数的异步调用;带参数的异步调用;调用返回Future的异步线程

2、@Async调用中的事务处理机制

@Async标注的方法,同时也适用了@Transactional进行了标注;

​ 在其调用数据库操作之时,将无法产生事务管理的控制,原因就在于其是基于异步处理的操作。

那该如何给这些操作添加事务管理呢?

​ 可以将需要事务管理操作的方法放置到异步方法内部,在内部被调用的方法上添加@Transactional. @Transactional需要用在调用方法上,用在异步方法上不会生效。

例如:

方法A,同时使用了@Async/@Transactional来标注,但是无法产生事务控制的目的。

方法B,使用了@Async来标注, B中调用了C、D,C/D分别使用@Transactional做了标注,则可实现事务控制的目的。

3、配合使用@EnableAsync

启动类或者Controller类加上@EnableAsync注解

@SpringBootApplication
@EnableAsync
public class Application {
   
   

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

}
@EnableAsync
@RestController
public class HelloController {
   
   
    
    @Autowired
    TestAsyncService testAsyncService;

}

@EnableAsync注解的意思是可以异步执行,就是开启多线程的意思。可以标注在方法、类上。@Async所修饰的函数不要定义为static类型,这样异步调用不会生效

4、举例:

比如需要调用一个发送短信的任务,实际短信是渠道方去发的,那么我们在把请求提交过去基本就结束了,这个时候就可以做一个异步的调用来实现。

先使用@EnableAsync来开启异步的支持,配置一个线程池:

Spring 4 中,对异步方法可以做一些配置,将配置类实现AsyncConfigurer 接口后,可以实现自定义线程池的功能,和统一处理异步方法的异常。

如果不限制并发数,可能会造成系统压力。

AsyncConfigurer 接口中的方法 Executor getAsyncExecutor() 实现自定义线程池。控制并发数。

AsyncConfigurer 接口中的方法 public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler()用于处理异步方法的异常。

AsyncUncaughtExceptionHandler 接口,只有一个方法:void handleUncaughtException(Throwable ex, Method method, Object… params);

因此,AsyncUncaughtExceptionHandler 接口可以认为是一个函数式接口,可以用拉姆达表达式实现该接口。当然不处理也是可以的,没用直接返回null

@Configuration
@EnableAsync
public class ThreadPoolConfig implements AsyncConfigurer {
   
   
     
    /**
     * ThreadFactory 为线程池创建的线程命名
     */
    private static ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("example-pool-%d").build();
    
    /**
     * 获取异步线程池执行对象
     */
    @Override
    public Executor getAsyncExecutor() {
   
   
        // 使用Spring内置线程池任务对象
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 设置线程池参数
        executor.initialize();
        // 获取到服务器的cpu内核
        int i = Runtime.getRuntime().availableProcessors();
        // 核心池大小
        executor.setCorePoolSize(5);
        // 最大线程数
        executor.setMaxPoolSize(100);
        // 队列容量
        executor.setQueueCapacity(1000);
        // 线程空闲时间(秒)
        executor.setKeepAliveSeconds(1000);
        // 线程前缀名称
        executor.setThreadNamePrefix("task-async-");
        // 设置拒绝策略:当pool已经达到max size的时候,如何处理新任务 CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
		executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
		// 等待所有任务结束后再关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
   
   
        return null;
//        return (throwable, method, objects) -> System.out.println(
//                "-- exception handler -- " + throwable + "-- method -- " + method + "-- objects -- " + objects);
    }
    
    
    /**
     * 自定义线程池
     */
    @Bean(name = "asyncTaskExecutor")
    public Executor asyncTaskExecutor() {
   
   
        //获取CPU 核心数
        int nThreads = Runtime.getRuntime().availableProcessors();
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                nThreads,
                2 * nThreads + 5,
                0L,
                TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(),
                threadFactory,
                new ThreadPoolExecutor.CallerRunsPolicy()); 
        // 先行创建符合corePoolSize参数值的线程数
        threadPoolExecutor.prestartAllCoreThreads();
        return threadPoolExecutor;
    }
}

如果我们项目中需要多个线程池,则可以参考asyncTaskExecutor()方法实例出自定义的线程池asyncTaskExecutor,在使用的时候使用@Async("asyncTaskExecutor")来实现使用asyncTaskExecutor的线程池实现异步操作。

@RestController
@RequestMapping("async")
public class AsyncController {
   
   

    @Autowired
    private AsyncServiceImpl asyncService;
 
    @RequestMapping("task")
    public String task() throws Exception {
   
   
        long currentTimeMillis = System.currentTimeMillis();
        System.out.println(Thread.currentThread().getName() + "主线程请求异步执行task1()");
        asyncService.task1();
        System.out.println(Thread.currentThread().getName() + "主线程请求异步执行task1()结束");
        System.out.println(Thread.currentThread().getName() + "主线程请求异步执行task2()");
        asyncService.task2();
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值