添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

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  //标注使用  
2 public void asyncMethodWithVoidReturnType() {  
3     System.out.println("Execute method asynchronously. "  
4       + Thread.currentThread().getName());  

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         //  
11     return null;  
 
 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();  
 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     }  

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;  
 6       用独立的线程来包装,@Async其本质就是如此  
 7     public void execute(Runnable task) {       
 8       executor.execute(createWrappedRunnable(task));  
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     }   
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     }  
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     }  

 分析: 可以发现其是实现了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实现

注解的应用范围:
  • 类:表示这个类中的所有方法都是异步的
  • 方法:表示这个方法是异步的,如果类也注解了,则以这个方法的注解为准
  • 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:不在新线程中执行任务,而是有调用者所在的线程来执行
    1. @Override  
    2. @Async("logExecutor")    //如果不指定名字,会使用缺省的“asyncExecutor”  
    3. public void saveUserOpLog(TabUserOpLog tabUserOpLog) {  
    4.   
    5.  userOpLogDAO.insertTabUserOpLog(tabUserOpLog);  
    6. }  
    (注意:如果在同一个类中调用的话,不会生效,原因请参考: http://blog.csdn.net/clementad/article/details/47339519) 其中void前面demo中已经写过,本篇文档主要写Future。 AsyncResult是异步方式,异步主要用于调用的代码需要长时间运行,才能返回结果的时候,可以不阻塞调用者。 打个比方,同步方式就是你打电话给客服,客服没法立刻解决,客服说你等等,别挂电话,然后等了10分钟,再告诉你。再挂电话。 此时电话费照收,并且你不能接打别人的电话。 异步方式就是,客服说,我先查查,查到了给你回电话,然后挂断。你干别的事情。等了10分钟,客服给你来电话了
    SpringBoot异步任务, 以及带返回值的异步任务(@Async 不起作用的原因) 第一部分: 返回值异步任务 当没有加入异步任务的时候,我们创建一个service ,里面的方法需要等待3秒才能完成, controller层写一个测试方法调用时间返回的接口, 直接调用, 下面是service层代码部分 package com.zz.amqp1.service; import org.springframework.stereotype.Service; import java.util.Dat
    第一部分: 返回值异步任务 当没有加入异步任务的时候,我们创建一个service ,里面的方法需要等待3秒才能完成, controller层写一个测试方法调用时间返回的接口, 直接调用, 下面是service层代码部分 package com.zz.amqp1.service; import org.springframework.stereotype.Ser...   博主简单数了下自己发布过的异步文章,已经断断续续 8 篇了,这次我想以 async返回类型为例,单独谈谈。   异步方法具有三个可让开发人员选择的返回类型:Task、Task 和 void。&nbsp;   什么时候需要使用哪一种返回类型,具体情况需要具体分析。如果使用不当,程序的执行结果也许并不是你想要的,下面我们就来好好谈谈如何针对不同的情况选择不同的返回类型。 &nbsp;