zoukankan      html  css  js  c++  java
  • springboot-async

    在项目中,当访问其他人的接口较慢或者做耗时任务时,不想程序一直卡在耗时任务上,想程序能够并行执行, 我们可以使用多线程来并行的处理任务,也可以使用spring提供的异步处理方式@Async。

    Spring异步线程池的接口类,其实质是java.util.concurrent.Executor

    Spring 已经实现的异常线程池:

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

    在异步处理的方法上添加注解@Async,就会启动一个新的线程去执行。

    开启异步配置

    SpringBoot中开启异步支持非常简单,只需要在配置类上面加上注解@EnableAsync,同时定义自己的线程池即可。 也可以不定义自己的线程池,则使用系统默认的线程池。这个注解可以放在Application启动类上,但是更推荐放在配置类上面。

    @Configuration
    @EnableAsync
    public class AsyncConfig implements AsyncConfigurer {
        // 省略...
    }

    异步处理方法分为不返回结果和返回结果,这两者的处理是有区别的。

    返回void

    没有结果返回的示例:

     1 @Component
     2 public class AsyncTask {
     3 
     4     private static final Logger LOGGER = LoggerFactory.getLogger(AsyncTask.class);
     5 
     6     @Async
     7     public void dealNoReturnTask() {
     8         LOGGER.info("返回值为void的异步调用开始" + Thread.currentThread().getName());
     9         try {
    10             Thread.sleep(3000);
    11         } catch (InterruptedException e) {
    12             e.printStackTrace();
    13         }
    14         LOGGER.info("返回值为void的异步调用结束" + Thread.currentThread().getName());
    15     }
    16 }

    返回Future

    异步调用返回数据,Future表示在未来某个点获取执行结果,返回数据类型可以自定义

     1 @Async
     2 public Future<String> dealHaveReturnTask(int i) {
     3     LOGGER.info("asyncInvokeReturnFuture, parementer=" + i);
     4     Future<String> future;
     5     try {
     6         Thread.sleep(1000 * i);
     7         future = new AsyncResult<>("success:" + i);
     8     } catch (InterruptedException e) {
     9         future = new AsyncResult<>("error");
    10     }
    11     return future;
    12 }

    测试异常捕获

    1 @Async
    2 public void testExpection() {
    3    throw new AsyncException("error");
    4 }

    异常处理

    我们可以实现AsyncConfigurer接口,也可以继承AsyncConfigurerSupport类来实现。

    在方法getAsyncExecutor()中创建线程池的时候,必须使用 executor.initialize(),不然在调用时会报线程池未初始化的异常。

     1 @Configuration
     2 @EnableAsync
     3 public class AsyncConfig implements AsyncConfigurer {
     4 
     5     @Override
     6     public Executor getAsyncExecutor() {
     7         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
     8         executor.setCorePoolSize(10);
     9         executor.setMaxPoolSize(100);
    10         executor.setQueueCapacity(100);
    11         executor.setWaitForTasksToCompleteOnShutdown(true);
    12         executor.setAwaitTerminationSeconds(60 * 10);
    13         executor.setThreadNamePrefix("AsyncThread-");
    14         executor.initialize(); //如果不初始化,导致找到不到执行器
    15         return executor;
    16     }
    17 
    18     @Override
    19     public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
    20         return new AsyncExceptionHandler();
    21     }
    22 }

    异步异常处理类:

     1 public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
     2 
     3     private static final Logger LOOGER = LoggerFactory.getLogger(AsyncExceptionHandler.class);
     4 
     5     @Override
     6     public void handleUncaughtException(Throwable ex, Method method, Object... params) {
     7         LOOGER.error("Async method has uncaught exception, params:{}" + Arrays.toString(params));
     8 
     9         if (ex instanceof AsyncException) {
    10             AsyncException asyncException = (AsyncException) ex;
    11             LOOGER.error("asyncException:"  + asyncException.getMessage());
    12         }
    13 
    14         LOOGER.error("Exception :", ex);
    15     }
    16 }

    异步处理异常类:

     1 public class AsyncException extends RuntimeException {
     2 
     3     public AsyncException() {
     4         super();
     5     }
     6 
     7     public AsyncException(String msg) {
     8         super(msg);
     9     }
    10 
    11     public AsyncException(int code, String msg) {
    12         super(msg);
    13         this.code = code;
    14     }
    15 
    16     private int code;
    17 
    18     public int getCode() {
    19         return code;
    20     }
    21 
    22     public void setCode(int code) {
    23         this.code = code;
    24     }
    25 
    26 }

    在调用方法时,可能出现方法中抛出异常的情况。Spring对于2种异步方法的异常处理机制如下:

    1. 对于方法返回值是Futrue的异步方法: a) 在调用future的get时捕获异常; b) 在异常方法中直接捕获异常
    2. 对于返回值是void的异步方法:通过AsyncUncaughtExceptionHandler处理异常

    测试代码

    最后写个测试代码看看是否跟预期一致:

     1 @RunWith(SpringRunner.class)
     2 @SpringBootTest
     3 public class SpringbootAsyncApplicationTests {
     4 
     5     private static final Logger LOGGER = LoggerFactory.getLogger(SpringbootAsyncApplicationTests.class);
     6 
     7     @Autowired
     8     private AsyncTask asyncTask;
     9 
    10     @Test
    11     public void testAsync() throws InterruptedException, ExecutionException {
    12 
    13         asyncTask.dealNoReturnTask();
    14 
    15         Future<String> f = asyncTask.dealHaveReturnTask(1);
    16 
    17         LOGGER.info("主线程执行finished");
    18 
    19         LOGGER.info(f.get());
    20         assertThat(f.get(), is("success:" + 1));
    21 
    22         asyncTask.testExpection();
    23     }
    24 }

    执行日志如下:

    [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService
    [           main] c.c.s.SpringbootAsyncApplicationTests    : 主线程执行finished
    [  AsyncThread-1] c.code.springbootasync.async.AsyncTask   : 返回值为void的异步调用开始AsyncThread-1
    [  AsyncThread-2] c.code.springbootasync.async.AsyncTask   : asyncInvokeReturnFuture, parementer=1
    [           main] c.c.s.SpringbootAsyncApplicationTests    : success:1
    [  AsyncThread-3] c.c.s.async.AsyncExceptionHandler        : Async method has uncaught exception, params:{}[]
    [  AsyncThread-3] c.c.s.async.AsyncExceptionHandler        : asyncException:error

    根据日志的线程名称很清楚的看出,每个异步方法在线程池的不同线程中执行。

    注:

    实际运行中,还出现过一个问题,一个Service中的方法调用自己的另一个方法,然后将这个方法加上@Async注解,然而并不起作用。 异步方法都应该放到单独的异步任务Bean里面去,然后将这个Bean注入到Service中即可。

     1 @Service
     2 public class DeviceService {
     3 
     4     @Resource
     5     private AsyncTask asyncTask;
     6     
     7     public int unbind(Integer id, ManagerInfo managerInfo) {
     8         // 前面省略...
     9         
    10         // 开始异步推送消息
    11         asyncTask.pushUnbindMsg(managerInfo, pos, location);
    12     }
    13 }

     

  • 相关阅读:
    第 15 章 标签页和工具提示插件
    第 14 章 下拉菜单和滚动监听插件
    第 13 章 模态框插件
    第 12 章 列表组面板和嵌入组件
    第 11 章 进度条媒体对象和 Well 组件
    第 10 章 巨幕页头缩略图和警告框组件
    第 9 章 路径分页标签和徽章组件
    lock()与lockInterruptibly()的区别
    MySQL中Innodb的聚簇索引和非聚簇索引
    MySQL使用可重复读作为默认隔离级别的原因
  • 原文地址:https://www.cnblogs.com/UniqueColor/p/10730701.html
Copyright © 2011-2022 走看看