在spring 3.x之后,内置了@Async,这个注解用于标注某个方法或某个类里面的所有方法都是需要异步处理的。被注解的方法被调用的时候,会在新线程中执行,而调用它的方法会在原来的线程中执行。这样可以避免阻塞、以及保证任务的实时性。适用于处理log、发送邮件、短信……等。

两种实现方式:注解和xml

一、通过注解实现

  1. @Configuration
  2. @EnableAsync
  3. public class SpringConfig {
  4. /** Set the ThreadPoolExecutor's core pool size. */
  5. private int corePoolSize = 10;
  6. /** Set the ThreadPoolExecutor's maximum pool size. */
  7. private int maxPoolSize = 200;
  8. /** Set the capacity for the ThreadPoolExecutor's BlockingQueue. */
  9. private int queueCapacity = 10;
  10. private String ThreadNamePrefix = "MyLogExecutor-";
  11. @Bean
  12. public Executor logExecutor() {
  13. ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
  14. executor.setCorePoolSize(corePoolSize);
  15. executor.setMaxPoolSize(maxPoolSize);
  16. executor.setQueueCapacity(queueCapacity);
  17. executor.setThreadNamePrefix(ThreadNamePrefix);
  18. // rejection-policy:当pool已经达到max size的时候,如何处理新任务
  19. // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
  20. executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
  21. executor.initialize();
  22. return executor;
  23. }
  24. }

1). 基于@Async无返回值调用

使用的方式非常简单,一个标注即可解决所有的问题:

1 @Async  //标注使用
2 public void asyncMethodWithVoidReturnType() {
3     System.out.println("Execute method asynchronously. "
4       + Thread.currentThread().getName());
5 }  

2). 基于@Async返回值的调用

 1 @Async
 2 public Future<String> asyncMethodWithReturnType() {
 3     System.out.println("Execute method asynchronously - "  + Thread.currentThread().getName());
 4     try {
 5         Thread.sleep(5000);
 6         return new AsyncResult<String>("hello world !!!!");
 7     } catch (InterruptedException e) {
 8         //
 9     }
10
11     return null;
12 }  

以上示例可以发现,返回的数据类型为Future类型,其为一个接口。具体的结果类型为AsyncResult,这个是需要注意的地方。

调用返回结果的异步方法示例

 1 public void testAsyncAnnotationForMethodsWithReturnType()
 2    throws InterruptedException, ExecutionException {
 3     System.out.println("Invoking an asynchronous method. "   + Thread.currentThread().getName());
 4     Future<String> future = asyncAnnotationExample.asyncMethodWithReturnType();
 5
 6     while (true) {  ///这里使用了循环判断,等待获取结果信息
 7         if (future.isDone()) {  //判断是否执行完毕
 8             System.out.println("Result from asynchronous process - " + future.get());
 9             break;
10         }
11         System.out.println("Continue doing something else. ");
12         Thread.sleep(1000);
13     }
14 }  

这些获取异步方法的结果信息,是通过不停的检查Future的状态来获取当前的异步方法是否执行完毕来实现的。

3). 基于@Async调用中的异常处理机制

在异步方法中,如果出现异常,对于调用者caller而言,是无法感知的。如果确实需要进行异常处理,则按照如下方法来进行处理:

1.  自定义实现AsyncTaskExecutor的任务执行器

在这里定义处理具体异常的逻辑和方式。

2.  配置由自定义的TaskExecutor替代内置的任务执行器

示例步骤1,自定义的TaskExecutor

 1 public class ExceptionHandlingAsyncTaskExecutor implements AsyncTaskExecutor {
 2     private AsyncTaskExecutor executor;
 3     public ExceptionHandlingAsyncTaskExecutor(AsyncTaskExecutor executor) {
 4         this.executor = executor;
 5      }
 6       用独立的线程来包装,@Async其本质就是如此
 7     public void execute(Runnable task) {
 8       executor.execute(createWrappedRunnable(task));
 9     }
10     public void execute(Runnable task, long startTimeout) {
11         /用独立的线程来包装,@Async其本质就是如此
12        executor.execute(createWrappedRunnable(task), startTimeout);
13     }
14     public Future submit(Runnable task) { return executor.submit(createWrappedRunnable(task));
15        //用独立的线程来包装,@Async其本质就是如此。
16     }
17     public Future submit(final Callable task) {
18       //用独立的线程来包装,@Async其本质就是如此。
19        return executor.submit(createCallable(task));
20     }
21
22     private Callable createCallable(final Callable task) {
23         return new Callable() {
24             public T call() throws Exception {
25                  try {
26                      return task.call();
27                  } catch (Exception ex) {
28                      handle(ex);
29                      throw ex;
30                    }
31                  }
32         };
33     }
34
35     private Runnable createWrappedRunnable(final Runnable task) {
36          return new Runnable() {
37              public void run() {
38                  try {
39                      task.run();
40                   } catch (Exception ex) {
41                      handle(ex);
42                    }
43             }
44         };
45     }
46     private void handle(Exception ex) {
47       //具体的异常逻辑处理的地方
48       System.err.println("Error during @Async execution: " + ex);
49     }
50 }

分析: 可以发现其是实现了AsyncTaskExecutor, 用独立的线程来执行具体的每个方法操作。在createCallable和createWrapperRunnable中,定义了异常的处理方式和机制。

handle()就是未来我们需要关注的异常处理的地方。 配置文件中的内容:

1 <task:annotation-driven executor="exceptionHandlingTaskExecutor" scheduler="defaultTaskScheduler" />
2 <bean id="exceptionHandlingTaskExecutor" class="nl.jborsje.blog.examples.ExceptionHandlingAsyncTaskExecutor">
3     <constructor-arg ref="defaultTaskExecutor" />
4 </bean>
5 <task:executor id="defaultTaskExecutor" pool-size="5" />
6 <task:scheduler id="defaultTaskScheduler" pool-size="1" />  

4). @Async调用中的事务处理机制

在@Async标注的方法,同时也适用了@Transactional进行了标注;在其调用数据库操作之时,将无法产生事务管理的控制,原因就在于其是基于异步处理的操作。

那该如何给这些操作添加事务管理呢?可以将需要事务管理操作的方法放置到异步方法内部,在内部被调用的方法上添加@Transactional.

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

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

二、通过xml实现

注解的应用范围:
  • 类:表示这个类中的所有方法都是异步的
  • 方法:表示这个方法是异步的,如果类也注解了,则以这个方法的注解为准
相关的配置:
<task:annotation-driven />配置:
  • executor:指定一个缺省的executor给@Async使用。
例子:
<task:annotation-driven executor="asyncExecutor" />
<task:executor />配置参数:
  • id:当配置多个executor时,被@Async("id")指定使用;也被作为线程名的前缀。
  • pool-size:
    • core size:最小的线程数,缺省:1
    • max size:最大的线程数,缺省:Integer.MAX_VALUE
  • queue-capacity:当最小的线程数已经被占用满后,新的任务会被放进queue里面,当这个queue的capacity也被占满之后,pool里面会创建新线程处理这个任务,直到总线程数达到了max size,这时系统会拒绝这个任务并抛出TaskRejectedException异常(缺省配置的情况下,可以通过rejection-policy来决定如何处理这种情况)。缺省值为:Integer.MAX_VALUE
  • keep-alive:超过core size的那些线程,任务完成后,再经过这个时长(秒)会被结束掉
  • rejection-policy:当pool已经达到max size的时候,如何处理新任务
    • ABORT(缺省):抛出TaskRejectedException异常,然后不执行
    • DISCARD:不执行,也不抛出异常
    • DISCARD_OLDEST:丢弃queue中最旧的那个任务
    • CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
配置例子:
<task:annotation-driven executor="asyncExecutor" />
<task:executor id="asyncExecutor" pool-size="100-10000" queue-capacity="10"/>
实例:
[html] view plain copy
  1. <!-- 缺省的异步任务线程池 -->
  2. <task:annotation-driven executor="asyncExecutor" />
  3. <task:executor id="asyncExecutor" pool-size="100-10000" queue-capacity="10" />
  4. <!-- 处理log的线程池 -->
  5. <task:executor id="logExecutor" pool-size="15-1000" queue-capacity="5" keep-alive="5"/>
[java] view plain copy
  1. @Override
  2. @Async("logExecutor")    //如果不指定名字,会使用缺省的“asyncExecutor”
  3. public void saveUserOpLog(TabUserOpLog tabUserOpLog) {
  4. userOpLogDAO.insertTabUserOpLog(tabUserOpLog);
  5. }

(注意:如果在同一个类中调用的话,不会生效,原因请参考:http://blog.csdn.net/clementad/article/details/47339519)

通过log可以看到,已经分开两个线程执行:
线程的优先级和类型:
优先级:NORM_PRIORITY
类型:非守护线程
用户线程(User Thread):JVM会等待所有的用户线程结束后才退出;当系统中没有用户线程了,JVM也就退出了
守护线程(Daemon Thread):一般是为其他线程提供服务的线程,比如GC垃圾回收器;JVM退出时,不会管守护线程是否存在,而是直接退出
所以,对于文件、数据库的操作,不适宜使用守护线程,不然可能会丢失数据!
Web应用停止时,Spring容器会被关闭,调用者如果是Spring bean,就会停止生成新任务。然而,线程池中已经在运行的任务,由于缺省是用户线程,所以JVM会等待它们结束后才退出。

Spring @Async配置4. 基于@Async无返回值调用 使用的方式非常简单,一个标注即可解决所有的问题: 1 @Async //标注使用 2 public void asyncMe相关推荐

  1. spring java配置_Spring基于java的配置

    我们之前都了解过spring基于xml的配置,我们也可以通过配置类来完成基于xml的配置,我们会在下面以一个例子来讲述一下Spring基于java的配置. 首先第一步准备工作: 1)创建一个Dog类 ...

  2. 为ASP.NET MVC配置基于Active Directory的表单认证方式

    为ASP.NET MVC配置基于Active Directory的表单认证方式 最近一直在研究基于Active Directory的表单认证方式,同时也在关注ASP.NET MVC的情况,同时也在应用 ...

  3. Java程序员从笨鸟到菜鸟之(七十二)细谈Spring(四)利用注解实现spring基本配置详解

    注:由于本人不大习惯注解方式,所以讲解完这里的注解实现基本配置之后,以后就不再单独把注解拿出来讲解了. 五:spring注解 1.准备工作 (1)导入common-annotations.jar (2 ...

  4. Spring Boot 线程池的创建、@Async 配置步骤及注意事项

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:终于放弃了单调的swagger-ui了,选择了这款神器-knife4j个人原创+1博客:点击前往,查看更多 作者 ...

  5. Spring及SpringBoot @Async配置步骤及注意事项

    前言 最近在做一个用户反馈的功能,就是当用户反馈意见或建议后服务端将意见保存然后发邮件给相关模块的开发者.考虑发邮件耗时的情况所以我想用异步的方法去执行,于是就开始研究Spring的@Async了.但 ...

  6. SpringBoot线程池的创建、@Async配置步骤及注意事项

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:Muscleheng blog.csdn.net/Muscl ...

  7. Spring Security 实战:基于配置的接口角色访问控制

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 作者 | 码农小胖哥 来源 | 公众号「码农小胖哥」 1 ...

  8. spring事物配置,声明式事务管理和基于@Transactional注解的使用

    事物管理对于企业应用来说是至关重要的,好使出现异常情况,它也可以保证数据的一致性. spring支持编程式事务管理和声明式事务管理两种方式. 编程式事务管理使用TransactionTemplate或 ...

  9. spring事务配置,声明式事务管理和基于@Transactional注解的使用

    事务管理对于企业应用来说是至关重要的,好使出现异常情况,它也可以保证数据的一致性. spring提供了几个关于事务处理的类: TransactionDefinition //事务属性定义 Transc ...

最新文章

  1. Django入门之开发环境搭建1.1
  2. 获取二叉树的所有叶子节点、获取全树深度与左右子树深度求解:递归
  3. java中web service的几种实现方式(自用)
  4. MySQL从入门到搞定实战
  5. CGContextRef学习笔记
  6. windows经典地雷小游戏(C语言实现)
  7. php di,PHP-DI中文文档(基于有道翻译,基本是直接拿过来使用,并没有润色)
  8. 停止Java线程,小心interrupt()方法
  9. 绘制条形图python_小白学Python(13)——pyecharts 绘制 柱状图/条形图 Bar
  10. python语言例子_【Python】SimPy的使用示例-Go语言中文社区
  11. 3.11 随机初始化
  12. 推荐一些好用的Chrome插件
  13. 清华计算机专业作业,微计算机技术(清华)配套练习题及答案 作业2(答案)END
  14. 4.2.7. Aggregate Expressions
  15. 实现圆形头像(并且不管图大小都显示成固定尺寸)-和上一篇有点不同。
  16. 3GPP TS 23501-g51 中英文对照 | 4.4.8 Time Sensitive Communication
  17. vulnhub Loly: 1
  18. 微信打开页面,提示到浏览器上打开
  19. Kafka 3.1的KRaft模式里的broker与controller
  20. 北邮考研复试机试准备过程(已上岸)

热门文章

  1. php strncmp,PHP中strncmp()函数比较两个字符串前2个字符是否相等的方法
  2. Golang入门第一天-工具准备
  3. 图的广度优先遍历和深度优先遍历
  4. 基础算法之二分法查找
  5. yaourt下载安装dropbox提示curl ssl证书错误
  6. 如何显示最近过生日的记录
  7. allow php tag,Smarty allow_php_tag报告'未定义的属性:Smarty :: $ allow_php_tag'
  8. SkyEye仿真ZYNQ芯片,轻松运行国产操作系统ReWorks
  9. 关于FPGA核心bug解决
  10. 9. GD32F103C8T6 定时器2的更新中断触发定时器0开始计时