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

Spring Boot利用@Async異步調(diào)用:ThreadPoolTaskScheduler線程池的優(yōu)雅關(guān)閉詳解

 更新時間:2018年05月02日 10:34:30   作者:翟永超  
這篇文章主要給大家介紹了關(guān)于Spring Boot利用@Async異步調(diào)用:ThreadPoolTaskScheduler線程池的優(yōu)雅關(guān)閉的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

前言

之前分享了一篇關(guān)于Spring Boot中使用@Async來實現(xiàn)異步任務(wù)和線程池控制的文章:《Spring Boot使用@Async實現(xiàn)異步調(diào)用:自定義線程池》。由于最近身邊也發(fā)現(xiàn)了不少異步任務(wù)沒有正確處理而導(dǎo)致的不少問題,所以在本文就接前面內(nèi)容,繼續(xù)說說線程池的優(yōu)雅關(guān)閉,主要針對ThreadPoolTaskScheduler線程池。

問題現(xiàn)象

在上篇文章的例子Chapter4-1-3中,我們定義了一個線程池,然后利用@Async注解寫了3個任務(wù),并指定了這些任務(wù)執(zhí)行使用的線程池。在上文的單元測試中,我們沒有具體說說shutdown相關(guān)的問題,下面我們就來模擬一個問題現(xiàn)場出來。

第一步:如前文一樣,我們定義一個ThreadPoolTaskScheduler線程池:

@SpringBootApplication
public class Application {
 public static void main(String[] args) {
 SpringApplication.run(Application.class, args);
 }

 @EnableAsync
 @Configuration
 class TaskPoolConfig {
 @Bean("taskExecutor")
 public Executor taskExecutor() {
  ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();
  executor.setPoolSize(20);
  executor.setThreadNamePrefix("taskExecutor-");
  return executor;
 }
 }
}

第二步:改造之前的異步任務(wù),讓它依賴一個外部資源,比如:Redis

@Slf4j
@Component
public class Task {

 @Autowired
 private StringRedisTemplate stringRedisTemplate;
 @Async("taskExecutor")
 public void doTaskOne() throws Exception {
 log.info("開始做任務(wù)一");
 long start = System.currentTimeMillis();
 log.info(stringRedisTemplate.randomKey());
 long end = System.currentTimeMillis();
 log.info("完成任務(wù)一,耗時:" + (end - start) + "毫秒");
 }

 @Async("taskExecutor")
 public void doTaskTwo() throws Exception {
 log.info("開始做任務(wù)二");
 long start = System.currentTimeMillis();
 log.info(stringRedisTemplate.randomKey());
 long end = System.currentTimeMillis();
 log.info("完成任務(wù)二,耗時:" + (end - start) + "毫秒");
 }

 @Async("taskExecutor")
 public void doTaskThree() throws Exception {
 log.info("開始做任務(wù)三");
 long start = System.currentTimeMillis();
 log.info(stringRedisTemplate.randomKey());
 long end = System.currentTimeMillis();
 log.info("完成任務(wù)三,耗時:" + (end - start) + "毫秒");
 }
}

注意:這里省略了pom.xml中引入依賴和配置redis的步驟

第三步:修改單元測試,模擬高并發(fā)情況下ShutDown的情況:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class ApplicationTests {
 @Autowired
 private Task task;
 @Test
 @SneakyThrows
 public void test() {
 for (int i = 0; i < 10000; i++) {
  task.doTaskOne();
  task.doTaskTwo();
  task.doTaskThree();

  if (i == 9999) {
  System.exit(0);
  }
 }
 }
}

說明:通過for循環(huán)往上面定義的線程池中提交任務(wù),由于是異步執(zhí)行,在執(zhí)行過程中,利用System.exit(0)來關(guān)閉程序,此時由于有任務(wù)在執(zhí)行,就可以觀察這些異步任務(wù)的銷毀與Spring容器中其他資源的順序是否安全。

第四步:運行上面的單元測試,我們將碰到下面的異常內(nèi)容。

org.springframework.data.redis.RedisConnectionFailureException: Cannot get Jedis connection; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
 at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.fetchJedisConnector(JedisConnectionFactory.java:204) ~[spring-data-redis-1.8.10.RELEASE.jar:na]
 at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.getConnection(JedisConnectionFactory.java:348) ~[spring-data-redis-1.8.10.RELEASE.jar:na]
 at org.springframework.data.redis.core.RedisConnectionUtils.doGetConnection(RedisConnectionUtils.java:129) ~[spring-data-redis-1.8.10.RELEASE.jar:na]
 at org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:92) ~[spring-data-redis-1.8.10.RELEASE.jar:na]
 at org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:79) ~[spring-data-redis-1.8.10.RELEASE.jar:na]
 at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:194) ~[spring-data-redis-1.8.10.RELEASE.jar:na]
 at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:169) ~[spring-data-redis-1.8.10.RELEASE.jar:na]
 at org.springframework.data.redis.core.RedisTemplate.randomKey(RedisTemplate.java:781) ~[spring-data-redis-1.8.10.RELEASE.jar:na]
 at com.didispace.async.Task.doTaskOne(Task.java:26) ~[classes/:na]
 at com.didispace.async.Task$$FastClassBySpringCGLIB$$ca3ff9d6.invoke(<generated>) ~[classes/:na]
 at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[spring-core-4.3.14.RELEASE.jar:4.3.14.RELEASE]
 at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
 at org.springframework.aop.interceptor.AsyncExecutionInterceptor$1.call(AsyncExecutionInterceptor.java:115) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
 at java.util.concurrent.FutureTask.run(FutureTask.java:266) [na:1.8.0_151]
 at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) [na:1.8.0_151]
 at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) [na:1.8.0_151]
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_151]
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_151]
 at java.lang.Thread.run(Thread.java:748) [na:1.8.0_151]
Caused by: redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
 at redis.clients.util.Pool.getResource(Pool.java:53) ~[jedis-2.9.0.jar:na]
 at redis.clients.jedis.JedisPool.getResource(JedisPool.java:226) ~[jedis-2.9.0.jar:na]
 at redis.clients.jedis.JedisPool.getResource(JedisPool.java:16) ~[jedis-2.9.0.jar:na]
 at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.fetchJedisConnector(JedisConnectionFactory.java:194) ~[spring-data-redis-1.8.10.RELEASE.jar:na]
 ... 19 common frames omitted
Caused by: java.lang.InterruptedException: null
 at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.reportInterruptAfterWait(AbstractQueuedSynchronizer.java:2014) ~[na:1.8.0_151]
 at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2088) ~[na:1.8.0_151]
 at org.apache.commons.pool2.impl.LinkedBlockingDeque.pollFirst(LinkedBlockingDeque.java:635) ~[commons-pool2-2.4.3.jar:2.4.3]
 at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:442) ~[commons-pool2-2.4.3.jar:2.4.3]
 at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:361) ~[commons-pool2-2.4.3.jar:2.4.3]
 at redis.clients.util.Pool.getResource(Pool.java:49) ~[jedis-2.9.0.jar:na]
 ... 22 common frames omitted

如何解決

原因分析

從異常信息JedisConnectionException: Could not get a resource from the pool來看,我們很容易的可以想到,在應(yīng)用關(guān)閉的時候異步任務(wù)還在執(zhí)行,由于Redis連接池先銷毀了,導(dǎo)致異步任務(wù)中要訪問Redis的操作就報了上面的錯。所以,我們得出結(jié)論,上面的實現(xiàn)方式在應(yīng)用關(guān)閉的時候是不優(yōu)雅的,那么我們要怎么做呢?

解決方法

要解決上面的問題很簡單,Spring的ThreadPoolTaskScheduler為我們提供了相關(guān)的配置,只需要加入如下設(shè)置即可:

@Bean("taskExecutor")
public Executor taskExecutor() {
 ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();
 executor.setPoolSize(20);
 executor.setThreadNamePrefix("taskExecutor-");
 executor.setWaitForTasksToCompleteOnShutdown(true);
 executor.setAwaitTerminationSeconds(60);
 return executor;
}

說明:setWaitForTasksToCompleteOnShutdown(true)該方法就是這里的關(guān)鍵,用來設(shè)置線程池關(guān)閉的時候等待所有任務(wù)都完成再繼續(xù)銷毀其他的Bean,這樣這些異步任務(wù)的銷毀就會先于Redis線程池的銷毀。同時,這里還設(shè)置了setAwaitTerminationSeconds(60),該方法用來設(shè)置線程池中任務(wù)的等待時間,如果超過這個時候還沒有銷毀就強制銷毀,以確保應(yīng)用最后能夠被關(guān)閉,而不是阻塞住。

完整示例:

讀者可以根據(jù)喜好選擇下面的兩個倉庫中查看Chapter4-1-4項目:

Github:https://github.com/dyc87112/SpringBoot-Learning/

Gitee:https://gitee.com/didispace/SpringBoot-Learning/

本地下載:http://xiazai.jb51.net/201805/yuanma/SpringBoot-Learning(jb51.net).rar

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關(guān)文章

  • java數(shù)據(jù)結(jié)構(gòu)關(guān)于棧的實例應(yīng)用

    java數(shù)據(jù)結(jié)構(gòu)關(guān)于棧的實例應(yīng)用

    大家好,本篇文章主要講的是java數(shù)據(jù)結(jié)構(gòu)關(guān)于棧的實例應(yīng)用,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽
    2021-12-12
  • SpringBoot3.x版本與Mybatis-Plus不兼容問題

    SpringBoot3.x版本與Mybatis-Plus不兼容問題

    當(dāng)使用3.x版本的SpringBoot結(jié)合Mybatis-Plus時版本不兼容就會報錯,本文就來介紹一下這個問題的解決方法,感興趣的可以了解一下
    2024-03-03
  • springboot 接口返回字符串帶引號的問題解決

    springboot 接口返回字符串帶引號的問題解決

    本文主要介紹了springboot 接口返回字符串帶引號的問題解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • Spring教程之refresh()執(zhí)行邏輯淺析

    Spring教程之refresh()執(zhí)行邏輯淺析

    這篇文章主要給大家介紹了關(guān)于Spring教程之refresh()執(zhí)行邏輯的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • SpringBoot與velocity的結(jié)合的示例代碼

    SpringBoot與velocity的結(jié)合的示例代碼

    本篇文章主要介紹了SpringBoot與velocity的結(jié)合的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-03-03
  • hadoop之MapReduce框架原理

    hadoop之MapReduce框架原理

    這篇文章主要介紹了hadoop的MapReduce框架原理,MapReduce是分為兩個階段的,MapperTask階段,和ReduceTask階段。如果有感興趣的小伙伴可以借鑒參考
    2023-03-03
  • SpringBoot中GlobalExceptionHandler異常處理機(jī)制詳細(xì)說明

    SpringBoot中GlobalExceptionHandler異常處理機(jī)制詳細(xì)說明

    Spring Boot的GlobalExceptionHandler是一個全局異常處理器,用于捕獲和處理應(yīng)用程序中發(fā)生的所有異常,這篇文章主要給大家介紹了關(guān)于Java中GlobalExceptionHandler異常處理機(jī)制的相關(guān)資料,需要的朋友可以參考下
    2024-03-03
  • Java生成短8位UUID的實現(xiàn)方案

    Java生成短8位UUID的實現(xiàn)方案

    在Java中,UUID通常用于生成全局唯一的標(biāo)識符,標(biāo)準(zhǔn)的UUID是128位的,由32個十六進(jìn)制數(shù)字組成,并通過特定的算法保證其在全球范圍內(nèi)的唯一性,本文給大家介紹了一個簡單的Java方法,用于生成一個較短的8位UUID,需要的朋友可以參考下
    2025-01-01
  • spring boot異步(Async)任務(wù)調(diào)度實現(xiàn)方法

    spring boot異步(Async)任務(wù)調(diào)度實現(xiàn)方法

    在沒有使用spring boot之前,我們的做法是在配置文件中定義一個任務(wù)池,然后將@Async注解的任務(wù)丟到任務(wù)池中去執(zhí)行,那么在spring boot中,怎么來實現(xiàn)異步任務(wù)的調(diào)用了,下面通過本文給大家講解,需要的朋友參考下
    2018-02-02
  • 深入了解Java 腳本化api編程

    深入了解Java 腳本化api編程

    Java 腳本 API 是一種獨立于框架的腳本語言,使用來自于Java代碼的腳本引擎 。可以使用Java語言編寫定制/可擴(kuò)展的應(yīng)用程序并將自定義腳本語言選擇留給最終用戶 。下面我們來詳細(xì)了解一下吧
    2019-06-06

最新評論

西乌珠穆沁旗| 浦县| 霞浦县| 关岭| 和林格尔县| 礼泉县| 陵水| 留坝县| 江孜县| 灵石县| 岳阳县| 南靖县| 右玉县| 千阳县| 平原县| 江门市| 水城县| 永平县| 婺源县| 望都县| 玛纳斯县| 吉水县| 达拉特旗| 钟祥市| 昔阳县| 宜州市| 阳新县| 军事| 柯坪县| 新邵县| 天津市| 从化市| 藁城市| 临清市| 兰州市| 徐汇区| 大英县| 西藏| 阳江市| 界首市| 十堰市|