zoukankan      html  css  js  c++  java
  • Spring @Async之三:Spring @Async使用方法总结

    引言: 在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的;但是在处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使用多线程来完成此类任务,其实,在spring 3.x之后,就已经内置了@Async来完美解决这个问题,本文将完成介绍@Async的用法。

    1.  何为异步调用?

        在解释异步调用之前,我们先来看同步调用的定义;同步就是整个处理过程顺序执行,当各个过程都执行完毕,并返回结果。 异步调用则是只是发送了调用的指令,调用者无需等待被调用的方法完全执行完毕;而是继续执行下面的流程。

         例如, 在某个调用中,需要顺序调用 A, B, C三个过程方法;如他们都是同步调用,则需要将他们都顺序执行完毕之后,方算作过程执行完毕; 如B为一个异步的调用方法,则在执行完A之后,调用B,并不等待B完成,而是执行开始调用C,待C执行完毕之后,就意味着这个过程执行完毕了。

    2.  常规的异步调用处理方式

        在Java中,一般在处理类似的场景之时,都是基于创建独立的线程去完成相应的异步调用逻辑,通过主线程和不同的线程之间的执行流程,从而在启动独立的线程之后,主线程继续执行而不会产生停滞等待的情况。

    3. @Async介绍

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

    注意: @Async所修饰的函数不要定义为static类型,这样异步调用不会生效,会报如下错误:

    从异常信息JedisConnectionException: Could not get a resource from the pool来看,我们很容易的可以想到,在应用关闭的时候异步任务还在执行,由于Redis连接池先销毁了,导致异步任务中要访问Redis的操作就报了上面的错。所以,我们得出结论,上面的实现方式在应用关闭的时候是不优雅的,那么我们要怎么做呢?如下设置:
    executor.setWaitForTasksToCompleteOnShutdown(true);
    executor.setAwaitTerminationSeconds(60);

    如何在Spring中启用@Async

    1、基于Java配置的启用方式:

    @Configuration  
    @EnableAsync  
    public class SpringAsyncConfig { ... }  

    springboot中的配置是:

    @EnableSwagger2
    @EnableAsync
    @EnableTransactionManagement
    public class SettlementApplication {
    
        public static void main(String[] args) {
            
            SpringApplication.run(SettlementApplication.class, args);
        }
        
        @Bean
        public RestTemplate restTemplate() {
            return new RestTemplate();
        }
        
    }

    2、基于XML配置文件的启用方式,配置如下:

    <task:executor id="myexecutor" pool-size="5"  />  
    <task:annotation-driven executor="myexecutor"/>  

     以上就是两种定义的方式。

    4. 基于@Async无返回值调用

        示例如下:

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

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

    5. 基于@Async返回值的调用

       示例如下:

    @Async  
    public Future<String> asyncMethodWithReturnType() {  
        System.out.println("Execute method asynchronously - "  
          + Thread.currentThread().getName());  
        try {  
            Thread.sleep(5000);  
            return new AsyncResult<String>("hello world !!!!");  
        } catch (InterruptedException e) {  
            //  
        }  
       
        return null;  
    }   
     

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

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

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

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

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

    在异步方法中,如果出现异常,对于调用者caller而言,是无法感知的

    看一个示例:

    A、异步方法类:

    /**
     * @author duanxz
     * 2018年8月2日 下午4:36:49
     */
    @Component
    public class AsyncServiceTest {
    
        @Async
        public void test() {
            System.out.println("AsyncServiceTest.test()");
            throw new IllegalArgumentException("sssssssssssssssssss");
        }
    }

    B、业务调用类

            //...
            test.test();
            System.out.println("channelUploadFileList()" + LocalDateTime.now());
            MLogModel model = new MLogModel();
           //...

    看结果:

    如果确实需要进行异常处理,则按照如下方法来进行处理:

        1.  自定义实现AsyncTaskExecutor的任务执行器,在这里定义处理具体异常的逻辑和方式。

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

        示例步骤1,自定义的TaskExecutor

    public class ExceptionHandlingAsyncTaskExecutor implements AsyncTaskExecutor {  
        private AsyncTaskExecutor executor;  
        public ExceptionHandlingAsyncTaskExecutor(AsyncTaskExecutor executor) {  
            this.executor = executor;  
         }  
          ////用独立的线程来包装,@Async其本质就是如此  
        public void execute(Runnable task) {       
          executor.execute(createWrappedRunnable(task));  
        }  
        public void execute(Runnable task, long startTimeout) {  
            /用独立的线程来包装,@Async其本质就是如此  
           executor.execute(createWrappedRunnable(task), startTimeout);           
        }   
        public Future submit(Runnable task) { return executor.submit(createWrappedRunnable(task));  
           //用独立的线程来包装,@Async其本质就是如此。  
        }   
        public Future submit(final Callable task) {  
          //用独立的线程来包装,@Async其本质就是如此。  
           return executor.submit(createCallable(task));   
        }   
          
        private Callable createCallable(final Callable task) {   
            return new Callable() {   
                public T call() throws Exception {   
                     try {   
                         return task.call();   
                     } catch (Exception ex) {   
                         handle(ex);   
                         throw ex;   
                       }   
                     }   
            };   
        }  
      
        private Runnable createWrappedRunnable(final Runnable task) {   
             return new Runnable() {   
                 public void run() {   
                     try {  
                         task.run();   
                      } catch (Exception ex) {   
                         handle(ex);   
                       }   
                }  
            };   
        }   
        private void handle(Exception ex) {  
          //具体的异常逻辑处理的地方  
          System.err.println("Error during @Async execution: " + ex);  
        }  
    } 

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

    handle()就是未来我们需要关注的异常处理的地方。

    配置文件中的内容:

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

    分析: 这里的配置使用自定义的taskExecutor来替代缺省的TaskExecutor。

    或者(先看看Aysnc的源码):

    public interface AsyncConfigurer {  
        Executor getAsyncExecutor();  
        AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler();  
    } 

    AsyncConfigurerSupport是AsyncConfigurer接口的实现但里边什么也没做。

    public class AsyncConfigurerSupport implements AsyncConfigurer {
    
        @Override
        public Executor getAsyncExecutor() {
            return null;
        }
    
        @Override
        public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
            return null;
        }
    
    }
    @Configuration  
    @EnableAsync  
    class SpringAsyncConfigurer extends AsyncConfigurerSupport {  
      
        @Bean  
        public ThreadPoolTaskExecutor asyncExecutor() {  
            ThreadPoolTaskExecutor threadPool = new ThreadPoolTaskExecutor();  
            threadPool.setCorePoolSize(3);  
            threadPool.setMaxPoolSize(3);  
            threadPool.setWaitForTasksToCompleteOnShutdown(true);  
            threadPool.setAwaitTerminationSeconds(60 * 15);  
            return threadPool;  
        }  
      
        @Override  
        public Executor getAsyncExecutor() {  
            return asyncExecutor;  
        }  
    } 

    可以自己实现AsyncConfigurer接口处理异常。

        @Configuration  
        @EnableAsync  
        public class SpringAsyncConfigurer implements AsyncConfigurer {  
               
            @Override  
            public Executor getAsyncExecutor() {  
                return new ThreadPoolTaskExecutor();  
            }  
          
            @Override  
            public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {  
                return new CustomAsyncExceptionHandler();  
            }  
          
        }  

    异常处理类:

        public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {  
           
            @Override  
            public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {  
                System.out.println("Exception message - " + throwable.getMessage());  
                System.out.println("Method name - " + method.getName());  
                for (Object param : obj) {  
                    System.out.println("Parameter value - " + param);  
                }  
            }  
               
        }  

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

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

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

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

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

    8. 异步线程池的定义

    8.1、一个线程池

        @Configuration  
        @EnableAsync  
        public class SpringAsyncConfig {  
            @Bean  
            public AsyncTaskExecutor taskExecutor() {  
                ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();  
                executor.setMaxPoolSize(10);  
                return executor;  
            }  
        }  

    8.2、多个线程池

        @Configuration  
        @EnableAsync  
        public class SpringAsyncConfig {  
               
            @Bean(name = "threadPoolTaskExecutor1")  
            public Executor threadPoolTaskExecutor() {  
                return new ThreadPoolTaskExecutor();  
            }  
               
            @Bean(name = "threadPoolTaskExecutor2")  
            public Executor threadPoolTaskExecutor() {  
                return new ThreadPoolTaskExecutor();  
            }  
          
        }  

    调用

    @Async("threadPoolTaskExecutor1")  
    public void asyncMethodWithConfiguredExecutor() {  
        System.out.println("Execute method with configured executor - "  
          + Thread.currentThread().getName());  
    }

    9. 总结

         通过以上的描述,应该对@Async使用的方法和注意事项了。

    转自:Spring中@Async用法总结

  • 相关阅读:
    HDU 5912 Fraction (模拟)
    CodeForces 722C Destroying Array (并查集)
    CodeForces 722B Verse Pattern (水题)
    CodeForces 722A Broken Clock (水题)
    CodeForces 723D Lakes in Berland (dfs搜索)
    CodeForces 723C Polycarp at the Radio (题意题+暴力)
    CodeForces 723B Text Document Analysis (水题模拟)
    CodeForces 723A The New Year: Meeting Friends (水题)
    hdu 1258
    hdu 2266 dfs+1258
  • 原文地址:https://www.cnblogs.com/duanxz/p/6782933.html
Copyright © 2011-2022 走看看