对于spring异步注解@Async的使用:

对于异步方法调用,从Spring3开始提供了@Async注解,该注解可以被标注在方法上,以便异步地调用该方法。调用者将在调用时立即返回,方法的实际执行将提交给Spring TaskExecutor的任务中,由指定的线程池中的线程执行。

我们尝试一次异步操作。

定义接口,我们采用有回调的future。

1 public interface AsyncService {
2
3 //涉及一个future的通道,以及一个计数使用的latch
4 Future<Integer> getFollowed(Integer times, CountDownLatch latch);
5
6 Future<Integer> getAtention(Integer times, CountDownLatch latch);
7
8 }

进行方法实现

其中一个我使用了默认线程池,另外一个指定自己的线程池稍后介绍区别

@Service
public class AsyncServiceImpl implements AsyncService { @Override
@Asyncpublic Future<Integer> getFollowed(Integer times, CountDownLatch latch) {
Integer count = 0;
//负责相关使用逻辑
for (int i = 0; i < times; i++) {
count += i;
}

latch.countDown();
return new AsyncResult<>(new Integer(count));
} @Override
@Async("asyncExecutor")
public Future<Integer> getAtention(Integer times, CountDownLatch latch) {
Integer count = 0;
//负责相关使用逻辑
for (int i = 0; i < times; i++) {
count += i;
}
latch.countDown();
return new AsyncResult<>(new Integer(count));
}

在外部设置好计数器返回现有结果

       long s = System.currentTimeMillis();
//计数
CountDownLatch latch = new CountDownLatch(2);
Future<Integer> atention = asyncService.getAtention(times,latch);
Future<Integer> followed = asyncService.getFollowed(times, latch);
long e = System.currentTimeMillis();
System.out.println("asyncService spend: " + (e - s));

定义自己异步线程池

@Configuration
public class AsyncThreadingConfig implements AsyncConfigurer { @Override
@Bean(name = "asyncExecutor")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
//最小线程数
taskExecutor.setCorePoolSize(5);
//最大线程数
taskExecutor.setMaxPoolSize(10);
//等待队列
taskExecutor.setQueueCapacity(25);
// 允许线程空闲时间(单位:默认为秒)
taskExecutor.setKeepAliveSeconds(30);
// 线程池名前缀
taskExecutor.setThreadNamePrefix("asyncExecutor-");
//设置拒绝策略
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//等待所有任务结束后再关闭线程池
taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
// 线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住
taskExecutor.setAwaitTerminationSeconds(60);
taskExecutor.initialize();
return taskExecutor;
} @Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return AsyncConfigurer.super.getAsyncUncaughtExceptionHandler();
} }

基本上就是异步实现的过程。

首先我们Spring 已经实现的线程池

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

Spring应用默认的线程池,指在@Async注解在使用时,不指定线程池的名称。查看源码,@Async的默认执行器为SimpleAsyncTaskExecutor

几种线程池会出现的问题:

在线程池应用中,参考阿里巴巴java开发规范:线程池不允许使用Executors去创建,不允许使用系统默认的线程池,推荐通过ThreadPoolExecutor的方式,这样的处理方式让开发的工程师更加明确线程池的运行规则,规避资源耗尽的风险。Executors各个方法的弊端:

newFixedThreadPool和newSingleThreadExecutor:主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。

newCachedThreadPool和newScheduledThreadPool:要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。

@Async默认异步配置使用的是SimpleAsyncTaskExecutor,该线程池默认来一个任务创建一个线程,若系统中不断的创建线程,最终会导致系统占用内存过高,引发OutOfMemoryError错误。针对线程创建问题,SimpleAsyncTaskExecutor提供了限流机制,通过concurrencyLimit属性来控制开关,当concurrencyLimit>=0时开启限流机制,默认关闭限流机制即concurrencyLimit=-1,当关闭情况下,会不断创建新的线程来处理任务。基于默认配置,SimpleAsyncTaskExecutor并不是严格意义的线程池,达不到线程复用的功能。

 @Async应用自定义线程池(第四步)

自定义线程池,可对系统中线程池更加细粒度的控制,方便调整线程池大小配置,线程执行异常控制和处理。在设置系统自定义线程池代替默认线程池时,虽可通过多种模式设置,但替换默认线程池最终产生的线程池有且只能设置一个(不能设置多个类继承AsyncConfigurer)。自定义线程池有如下模式:

  • 重新实现接口AsyncConfigurer
  • 继承AsyncConfigurerSupport
  • 配置由自定义的TaskExecutor替代内置的任务执行器

通过查看Spring源码关于@Async的默认调用规则,会优先查询源码中实现AsyncConfigurer这个接口的类,实现这个接口的类为AsyncConfigurerSupport。但默认配置的线程池和异步处理方法均为空,所以,无论是继承或者重新实现接口,都需指定一个线程池。且重新实现 public Executor getAsyncExecutor()方法。

Async可能会存在失效

没有过去到代理类,本类调用时,直接自己内部调用,没有走代理类

1.没有在@SpringBootApplication启动类当中添加注解@EnableAsync注解。

2.异步方法使用注解@Async的返回值只能为void或者Future。

3.没有走Spring的代理类。因为@Transactional和@Async注解的实现都是基于Spring的AOP,而AOP的实现是基于动态代理模式实现的。那么注解失效的原因就很明显了,有可能因为调用方法的是对象本身而不是代理对象,因为没有经过Spring容器。

异步注解@Async使用及其部分调优的更多相关文章

  1. Spring中异步注解@Async的使用、原理及使用时可能导致的问题

    前言 其实最近都在研究事务相关的内容,之所以写这么一篇文章是因为前面写了一篇关于循环依赖的文章: <面试必杀技,讲一讲Spring中的循环依赖> 然后,很多同学碰到了下面这个问题,添加了S ...

  2. springboot:使用异步注解@Async的那些坑

    springboot:使用异步注解@Async的那些坑 一.引言 在java后端开发中经常会碰到处理多个任务的情况,比如一个方法中要调用多个请求,然后把多个请求的结果合并后统一返回,一般情况下调用其他 ...

  3. springboot:嵌套使用异步注解@Async还会异步执行吗

    一.引言 在前边的文章<[springboot:使用异步注解@Async的那些坑>中介绍了使用@Async注解获取任务执行结果的错误用法,今天来分享下另外一种常见的错误. 二.代码演示 下 ...

  4. springboot:使用异步注解@Async的前世今生

    在前边的文章中,和小伙伴一起认识了异步执行的好处,以及如何进行异步开发,对,就是使用@Async注解,在使用异步注解@Async的过程中也存在一些坑,不过通过正确的打开方式也可以很好的避免,今天想和大 ...

  5. 关于Dubbo和Spring异步注解@Async的冲突

    项目中难免会有异步处理的需求,像异步记录日志啦,异步发送邮件啦,而Dubbo又是现在主流的分布式框架,所有异步+Dubbo的组合是再所难免的 但博主是实践中发现Dubbo的服务并不能很好的跟Sprin ...

  6. Java中异步注解@Async的陷阱

    或许,你在Java后端添加异步过程时会这样处理,然后摇摇大摆.灰溜溜地闪,而实际的运行结果却并不是我们期望的那样.那么,现在就将试验结果记录如下,以便少走弯路. (一)在Controller层的公开接 ...

  7. @Async异步注解与SpringBoot结合使用

    当你在service层需要启动异步线程去执行某些分支任务,又不希望显式使用Thread等线程相关类,只想专注于实现业务逻辑代码开发,可以使用@Async异步注解. 1. 使用@Async 异步注解 C ...

  8. async异步注解和aspect切面注解等注解的原理

    在我们使用spring框架的过程中,在很多时候我们会使用@async注解来异步执行某一些方法,提高系统的执行效率.今天我们来探讨下spring是如何完成这个功能的. 1.spring 在扫描bean的 ...

  9. 如何在项目中使用Spring异步调用注解@Async

    本文主要介绍如何使用Spring框架提供的异步调用注解@Async,异步线程池配置.异常捕获处理. 开启@Async注解支持 使用@Async注解的之前,必须在项目中启动时调用@EnableAsync ...

  10. spring boot使用@Async异步注解

    1.java的大部分接口的方法都是串行执行的,但是有些业务场景是不需要同步返回结果的,可以把结果直接返回,具体业务异步执行,也有些业务接口是需要并行获取数据,最后把数据聚合在统一返回给前端. 通常我们 ...

随机推荐

  1. vue+高德地图配置及添加marker

    1.首先在index.html中引入高德地图 <script type="text/javascript" src="https://webapi.amap.com ...

  2. CSS 选择器-认识并应用选择器

    在内嵌式和外部css中,要想将CSS样式应用于特定的HTML元素,首先需要找到该目标元素,这时需要用到CSS中的选择器. 选择器:选择要添加样式的 HTML 标签的一种方法.模式. 首先学习 css2 ...

  3. Hive 操作与应用 词频统计

    一.hive用本地文件进行词频统计 1.准备本地txt文件 2.启动hadoop,启动hive 3.创建数据库,创建文本表 4.映射本地文件的数据到文本表中 5.hql语句进行词频统计交将结果保存到结 ...

  4. K8SYaml文件详解

    一.K8S支持的文件格式 kubernetes支持YAML和JSON文件格式管理资源对象. JSON格式:主要用于api接口之间消息的传递 YAML格式:用于配置和管理,YAML是一种简洁的非标记性语 ...

  5. Linux CentOS Docker 安装、加载配置

    Docker Version:2.10.2 OS: CentOS 7 1.卸载 $ sudo yum remove docker \ docker-client \ docker-client-lat ...

  6. java的内存模型,jmm理解和(GC)垃圾回收时机。

    jmm模型中的gc处理是在堆中回收. 1.新对象出来以后,先尝试在eden中放下,放不下的时候,进行一次ygc,只会在eden中回收,

  7. C# 自定义控件如何正确的继承父类

    C# 自定义控件可以分为三类: 复合控件:基本控件组合而成.应当继承自 UserControl 扩展控件:继承基本控件,扩展一些属性和事件.比如继承 Button 自定义控件:直接继承自 Contro ...

  8. PriorityQueue&&Function overload

    用 STL 里面堆算法实现的与真正的STL里面的 priority_queue用法相似的priority_queue    #include <iostream>    #include ...

  9. 4.Vue组件

    一.组件化开发概述 1.组件化开发思想 标准 分治:不同的功能分配到不同的组件中 重用: 组合 2.编程中的组件化思想体现 3.组件化规范: Web Components 我们希望尽可能多的重用代码 ...

  10. mysql 设置查询超时配置

    mysql设置查询超时 mysql5.6: max_statement_time (毫秒) mysql5.7: max_execution_time(毫秒) mariadb: max_statemen ...