SpringBoot日常:注解@Async的使用

背景

日常开发中我们经常会遇到一些业务逻辑耗时较长、或者是和主体逻辑关联并不强、又或者是不需要立即得到执行结果。如:邮件通知、日志记录、数据同步。这时我们可以用到注解@Async。我们在使用该注解时,不加任何参数指定线程池,则会默认使用Spring自带的 SimpleAsyncTaskExecutor 线程池,当并发大的时候会不断的创建线程导致严重影响性能。所以日常开发规范都要求异步显性指定线程池。

简介

@Async是Spring的注解,可以加在类或方法上。通俗的来讲,如果加上了这个注解,那么该类或者该方法在使用时将会进行异步处理,也就是创建一个线程来实现这个类或者方法,实现多线程。

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Async {
    String value() default "";
}

线程池的执行逻辑:
在这里插入图片描述

spring 已经实现的线程池

  • SimpleAsyncTaskExecutor:不是真的线程池,这个类不重用线程,默认每次调用都会创建一个新的线程。
  • SyncTaskExecutor:这个类没有实现异步调用,只是一个同步操作。只适用于不需要多线程的地方
  • ConcurrentTaskExecutor:Executor的适配类,不推荐使用。如果ThreadPoolTaskExecutor不满足要求时,才用考虑使用这个类。
  • SimpleThreadPoolTaskExecutor:是Quartz的SimpleThreadPool的类。线程池同时被quartz和非quartz使用,才需要使用此类。
  • ThreadPoolTaskExecutor :最常使用,推荐。其实质是对java.util.concurrent.ThreadPoolExecutor的包装。

使用方式:

第一种(不推荐):

使用的是Spring默认的线程池。

接入步骤:
1、需要在@SpringBootApplication启动类或者@configure注解类上 添加注解@EnableAsync启动多线程注解。
2、在需要异步执行的方法上添加@Async注解。


	//启动类加上@EnableAsync注解
	@EnableAsync
	@SpringBootApplication
	public class BizAdminApplication {
	    public static void main(String[] args) {
	        SpringApplication.run(BizAdminApplication.class, args);
	    }
	
	}

	//controller 方法接口
	@GetMapping(value = "/testAsync", produces = MediaType.APPLICATION_JSON_VALUE)
	public String testAsync() {
	    centerService.testAsync();
	    return "success";
	}

	//异步执行方法
	@Async
	public  void  testAsync() {
	    try {
	    	System.out.println(Thread.currentThread());
	        Thread.sleep(2000);
	        System.out.println("异步发送邮件");
	    } catch (Exception e) {
	    }
	}

执行后输出的结果
在这里插入图片描述

第二种(推荐):

使用自定义的线程池。

接入步骤:
1、创建一个配置类ThreadPoolTaskConfig,并在该配置类上加上注解@EnableAsync,表示启动多线程。
2、在需要异步执行的方法上添加@Async注解,并指定线程池名称


//添加一个配置类
@Configuration
@EnableAsync
public class ThreadPoolTaskConfig {

    //核心线程数(默认线程数)
    private static final int CORE_POOL_SIZE = 15;

    //最大线程数
    private static final int MAX_POOL_SIZE = 100;

    //允许线程空闲时间(单位:默认为秒)
    private static final int KEEP_ALIVE_TIME = 8;

    //缓冲队列大小
    private static final int QUEUE_CAPACITY = 150;

    //线程池名前缀
    private static final String THREAD_NAME_PREFIX = "Async-Service-";

	// bean的名称,默认为首字母小写的方法名
    @Bean("taskExecutor") 
    public ThreadPoolTaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(CORE_POOL_SIZE);
        executor.setMaxPoolSize(MAX_POOL_SIZE);
        executor.setQueueCapacity(QUEUE_CAPACITY);
        executor.setKeepAliveSeconds(KEEP_ALIVE_TIME);
        executor.setThreadNamePrefix(THREAD_NAME_PREFIX);

        // 线程池对拒绝任务的处理策略
        // CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 初始化
        executor.initialize();
        return executor;
    }
}


	//controller 方法接口
	@GetMapping(value = "/testAsync", produces = MediaType.APPLICATION_JSON_VALUE)
	public String testAsync() {
	    centerService.testAsync();
	    return "success";
	}

	//异步执行方法
	@Async(value = "taskExecutor")
	public  void  testAsync() {
	    try {
	    	System.out.println(Thread.currentThread());
	        Thread.sleep(2000);
	        System.out.println("异步发送邮件");
	    } catch (Exception e) {
	    }
	}

执行后输出的结果
在这里插入图片描述

注意事项

1、@Async会存在一些失效的情况

  • 注解@Async的方法不是public方法;
  • 注解@Async的返回值只能为void或Future;
  • 注解@Async方法使用static修饰也会失效;
  • 启动类没加@EnableAsync注解;
  • 调用方和@Async不能在一个类中;
  • 注解@Async所在类的使用需要自动注入,如果手动new会失效
  • 在Async方法上标注@Transactional是没用的,但在Async方法调用的方法上标注@Transcational是有效的;

2、无返回值调用
对于返回值是void,异常会被AsyncUncaughtExceptionHandler处理掉,如果我们需要抛异常,则手动new一个异常抛出。

	@Async(value = "taskExecutor")
	public  void  testAsync(String msg) {
	    try {
	        System.out.println("异步发送邮件");
	        throw new IllegalArgumentException(msg);
	    } catch (Exception e) {
	    }
	}

3、有返回值Future调用
对于返回值是Future,不会被AsyncUncaughtExceptionHandler处理,需要我们在方法中捕获异常并处理或者在调用方在调用Futrue.get时捕获异常进行处理

	@Async(value = "taskExecutor")
	public Future<String> testAsync(String msg) {
		Future<String> future;
	    try {
	        Thread.sleep(2000);
	        System.out.println("异步发送邮件");
	        future = new AsyncResult<String>("success:" + i);
	        throw new IllegalArgumentException(msg);
	    } catch (InterruptedException e) {
	        future = new AsyncResult<String>("error");
	    } catch(IllegalArgumentException e){
	        future = new AsyncResult<String>("error-IllegalArgumentException");
	    }
    	return future;
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值