最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring Boot 異步框架的使用詳解

 更新時間:2019年01月17日 14:21:19   作者:JavaDog  
這篇文章主要介紹了Spring Boot 異步框架的使用詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

1. 前言

隨著數(shù)據(jù)量和調(diào)用量的增長,用戶對應(yīng)用的性能要求越來越高。另外,在實際的服務(wù)中,還存在著這樣的場景:系統(tǒng)在組裝數(shù)據(jù)的時候,對于數(shù)據(jù)的各個部分的獲取實際上是沒有前后依賴關(guān)系的。這些問題都很容易讓我們想到將這些同步調(diào)用全都改造為異步調(diào)用。不過自己實現(xiàn)起來比較麻煩,還容易出錯。好在Spring已經(jīng)提供了該問題的解決方案,而且使用起來十分方便。

2.Spring異步執(zhí)行框架的使用方法

2.1 maven 依賴

Spring異步執(zhí)行框架的相關(guān)bean包含在spring-context和spring-aop模塊中,所以只要引入上述的模塊即可。

2.2 開啟異步任務(wù)支持

Spring提供了@EnableAsync的注解來標注是否啟用異步任務(wù)支持。使用方式如下:

@Configuration
@EnableAsync
public class AppConfig {
}

Note: @EnableAsync必須要配合@Configuration使用,否則會不生效

2.3 方法標記為異步調(diào)用

將同步方法的調(diào)用改為異步調(diào)用也很簡單。對于返回值為void的方法,直接加上@Async注解即可。對于有返回值的方法,除了加上上述的注解外,還需要將方法的返回值修改為Future類型和將返回值用AsyncResult包裝起來。如下所示:

// 無返回值的方法直接加上注解即可。 
@Async
public void method1() {
 ...
}

// 有返回值的方法需要修改返回值。
@Async
public Future<Object> method2() {
 ...
 return new AsyncResult<>(Object);
}  

2.4 方法調(diào)用

對于void的方法,和普通的調(diào)用沒有任何區(qū)別。對于非void的方法,由于返回值是Future類型,所以需要用get()方法來獲取返回值。如下所示:

public static void main(String[] args) {
  service.method1();
  Future<Object> futureResult = service.method2();
  Object result;
  try {
     result = futureResult.get(); 
    } catch (InterruptedException | ExecutionException e) {
      ...
    }
}

3. 原理簡介

這塊的源碼的邏輯還是比較簡單的,主要是Spring幫我們生成并管理了一個線程池,然后方法調(diào)用的時候使用動態(tài)代理將方法的執(zhí)行包裝為Callable類型并提交到線程池中執(zhí)行。核心的實現(xiàn)邏輯在AsyncExecutionInterceptor類的invoke()方法中。如下所示:

@Override
public Object invoke(final MethodInvocation invocation) throws Throwable {
  Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
  Method specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass);
  final Method userDeclaredMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);

  AsyncTaskExecutor executor = determineAsyncExecutor(userDeclaredMethod);
  if (executor == null) {
    throw new IllegalStateException(
        "No executor specified and no default executor set on AsyncExecutionInterceptor either");
  }

  Callable<Object> task = new Callable<Object>() {
    @Override
    public Object call() throws Exception {
      try {
        Object result = invocation.proceed();
        if (result instanceof Future) {
          return ((Future<?>) result).get();
        }
      }
      catch (ExecutionException ex) {
        handleError(ex.getCause(), userDeclaredMethod, invocation.getArguments());
      }
      catch (Throwable ex) {
        handleError(ex, userDeclaredMethod, invocation.getArguments());
      }
      return null;
    }
  };

  return doSubmit(task, executor, invocation.getMethod().getReturnType());
}

4.自定義taskExecutor及異常處理

4.1自定義taskExecutor

Spring查找TaskExecutor邏輯是:

1. 如果Spring context中存在唯一的TaskExecutor bean,那么就使用這個bean。

2. 如果1中的bean不存在,那么就會查找是否存在一個beanName為taskExecutor且是java.util.concurrent.Executor實例的bean,有則使用這個bean。

3. 如果1、2中的都不存在,那么Spring就會直接使用默認的Executor,即SimpleAsyncTaskExecutor。

在第2節(jié)的實例中,我們直接使用的是Spring默認的TaskExecutor。但是對于每一個新的任務(wù),SimpleAysncTaskExecutor都是直接創(chuàng)建新的線程來執(zhí)行,所以無法重用線程。具體的執(zhí)行的代碼如下:

@Override
public void execute(Runnable task, long startTimeout) {
  Assert.notNull(task, "Runnable must not be null");
  Runnable taskToUse = (this.taskDecorator != null ? this.taskDecorator.decorate(task) : task);
  if (isThrottleActive() && startTimeout > TIMEOUT_IMMEDIATE) {
    this.concurrencyThrottle.beforeAccess();
    doExecute(new ConcurrencyThrottlingRunnable(taskToUse));
  }
  else {
    doExecute(taskToUse);
  }
}

protected void doExecute(Runnable task) {
  Thread thread = (this.threadFactory != null ? this.threadFactory.newThread(task) : createThread(task));
  thread.start();
} 

所以我們在使用的時候,最好是使用自定義的TaskExecutor。結(jié)合上面描述的Spring查找TaskExecutor的邏輯,最簡單的自定義的方法是使用@Bean注解。示例如下:

// ThreadPoolTaskExecutor的配置基本等同于線程池
@Bean("taskExecutor")
public Executor getAsyncExecutor() {
  ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
  taskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
  taskExecutor.setCorePoolSize(CORE_POOL_SIZE);
  taskExecutor.setQueueCapacity(CORE_POOL_SIZE * 10);
  taskExecutor.setThreadNamePrefix("wssys-async-task-thread-pool");
  taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
  taskExecutor.setAwaitTerminationSeconds(60 * 10);
  taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
  return taskExecutor;
}

另外,Spring還提供了一個AsyncConfigurer接口,通過實現(xiàn)該接口,除了可以實現(xiàn)自定義Executor以外,還可以自定義異常的處理。代碼如下:

@Configuration
@Slf4j
public class AsyncConfig implements AsyncConfigurer {

  private static final int MAX_POOL_SIZE = 50;

  private static final int CORE_POOL_SIZE = 20;

  @Override
  @Bean("taskExecutor")
  public Executor getAsyncExecutor() {
    ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();

    taskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
    taskExecutor.setCorePoolSize(CORE_POOL_SIZE);
    taskExecutor.setQueueCapacity(CORE_POOL_SIZE * 10);
    taskExecutor.setThreadNamePrefix("async-task-thread-pool");
    taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
    taskExecutor.setAwaitTerminationSeconds(60 * 10);
    taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
    return taskExecutor;
  }

  @Override
  public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
    return (ex, method, params) -> log.error("invoke async method occurs error. method: {}, params: {}",
      method.getName(), JSON.toJSONString(params), ex);
  }

}

Note:

Spring還提供了一個AsyncConfigurerSupport類,該類也實現(xiàn)了AsyncConfigurer接口,且方法的返回值都是null,旨在提供一個方便的實現(xiàn)。

當getAsyncExecutor()方法返回null的時候,Spring會使用默認的處理器(強烈不推薦)。

當getAsyncUncaughtExceptionHandler()返回null的時候,Spring會使用SimpleAsyncUncaughtExceptionHandler來處理異常,該類會打印出異常的信息。

所以對該類的使用,最佳的實踐是繼承該類,并且覆蓋實現(xiàn)getAsyncExecutor()方法。

4.2 異常處理

Spring異步框架對異常的處理如下所示:

// 所在類:AsyncExecutionAspectSupport
protected void handleError(Throwable ex, Method method, Object... params) throws Exception {
  if (Future.class.isAssignableFrom(method.getReturnType())) {
    ReflectionUtils.rethrowException(ex);
  }
  else {
    // Could not transmit the exception to the caller with default executor
    try {
      this.exceptionHandler.handleUncaughtException(ex, method, params);
    }
    catch (Throwable ex2) {
      logger.error("Exception handler for async method '" + method.toGenericString() +
          "' threw unexpected exception itself", ex2);
    }
  }
}

從代碼來看,如果返回值是Future類型,那么直接將異常拋出。如果返回值不是Future類型(基本上包含的是所有返回值void類型的方法,因為如果方法有返回值,必須要用Future包裝起來),那么會調(diào)用handleUncaughtException方法來處理異常。

注意:在handleUncaughtException()方法中拋出的任何異常,都會被Spring Catch住,所以沒有辦法將void的方法再次拋出并傳播到上層調(diào)用方的?。。?/p>

關(guān)于Spring 這個設(shè)計的緣由我的理解是:既然方法的返回值是void,就說明調(diào)用方不關(guān)心方法執(zhí)行是否成功,所以也就沒有必要去處理方法拋出的異常。如果需要關(guān)心異步方法是否成功,那么返回值改為boolean就可以了。

4.4 最佳實踐的建議

  1. @Async可以指定方法執(zhí)行的Executor,用法:@Async("MyTaskExecutor")。推薦指定Executor,這樣可以避免因為Executor配置沒有生效而Spring使用默認的Executor的問題。
  2. 實現(xiàn)接口AsyncConfigurer的時候,方法getAsyncExecutor()必須要使用@Bean,并指定Bean的name。如果不使用@Bean,那么該方法返回的Executor并不會被Spring管理。用java doc api的原話是:is not a fully managed Spring bean.(具體含義沒有太理解,不過親測不加這個注解無法正常使用)
  3. 由于其本質(zhì)上還是基于代理實現(xiàn)的,所以如果一個類中有A、B兩個異步方法,而A中存在對B的調(diào)用,那么調(diào)用A方法的時候,B方法不會去異步執(zhí)行的。
  4. 在異步方法上標注@Transactional是無效的。
  5. future.get()的時候,最好使用get(long timeout, TimeUnit unit)方法,避免長時間阻塞。
  6. ListenableFuture和CompletableFuture也是推薦使用的,他們相比Future,提供了對異步調(diào)用的各個階段或過程進行介入的能力。

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • idea maven編譯報錯Java heap space的解決方法

    idea maven編譯報錯Java heap space的解決方法

    這篇文章主要為大家詳細介紹了idea maven編譯報錯Java heap space的相關(guān)解決方法,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2025-04-04
  • Java并發(fā)編程中的ReentrantLock詳解

    Java并發(fā)編程中的ReentrantLock詳解

    這篇文章主要介紹了Java并發(fā)編程中的ReentrantLock詳解,從Java 5 開始,引入了一個高級的處理并發(fā)的java.util.concurrent包,它提供了大量更高級的并發(fā)功能,能大大簡化多線程程序的編寫,需要的朋友可以參考下
    2023-11-11
  • idea中Java實體類怎樣生成序列化的版本號的方法

    idea中Java實體類怎樣生成序列化的版本號的方法

    這篇文章主要介紹了idea中Java實體類怎樣生成序列化的版本號的方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-11-11
  • springMVC實現(xiàn)文件上傳和下載

    springMVC實現(xiàn)文件上傳和下載

    這篇文章主要為大家詳細介紹了springMVC實現(xiàn)文件上傳和下載,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • Java中常用的設(shè)計模式之模板模式詳解

    Java中常用的設(shè)計模式之模板模式詳解

    這篇文章主要為大家詳細介紹了Python實現(xiàn)學生成績管理系統(tǒng),使用數(shù)據(jù)庫,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • java實現(xiàn)pdf按頁轉(zhuǎn)換為圖片

    java實現(xiàn)pdf按頁轉(zhuǎn)換為圖片

    這篇文章主要為大家詳細介紹了java實現(xiàn)pdf按頁轉(zhuǎn)換為圖片,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-12-12
  • java Spring整合Freemarker的詳細步驟

    java Spring整合Freemarker的詳細步驟

    本文對Spring整合Freemarker步驟做了詳細的說明,按步驟操作一定可以整合通過,這里提供給大家做參考
    2013-11-11
  • 實現(xiàn)quartz定時器及quartz定時器原理介紹

    實現(xiàn)quartz定時器及quartz定時器原理介紹

    Quartz是一個大名鼎鼎的Java版開源定時調(diào)度器,功能強悍,使用方便,下面我們看看如何使用它
    2013-12-12
  • Java OOM 異常場景與排查過程(堆、棧、方法區(qū))

    Java OOM 異常場景與排查過程(堆、棧、方法區(qū))

    這篇文章主要介紹了Java OOM 異常場景與排查過程(堆、棧、方法區(qū)),具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • Spring實戰(zhàn)之SpEl語法實例詳解

    Spring實戰(zhàn)之SpEl語法實例詳解

    這篇文章主要介紹了Spring實戰(zhàn)之SpEl語法,結(jié)合實例形式分析了SpEl創(chuàng)建數(shù)組、集合及解析變量等相關(guān)操作原理與實現(xiàn)技巧,需要的朋友可以參考下
    2019-12-12

最新評論

大港区| 高唐县| 榆社县| 瑞昌市| 赣榆县| 武川县| 电白县| 巴林右旗| 临邑县| 丹凤县| 花莲县| 迁安市| 揭西县| 蓬莱市| 滕州市| 吐鲁番市| 荔浦县| 三明市| 峨眉山市| 新河县| 淄博市| 禹城市| 宁武县| 三原县| 桃江县| 精河县| 宁阳县| 龙南县| 武川县| 榆社县| 斗六市| 宣化县| 合作市| 花莲市| 梓潼县| 溧阳市| 乌鲁木齐县| 辽宁省| 汉川市| 多伦县| 边坝县|