亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > Java > 正文

Spring Boot利用@Async異步調用:ThreadPoolTaskScheduler線程池的優雅關閉詳解

2024-07-14 08:40:33
字體:
來源:轉載
供稿:網友

前言

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

問題現象

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

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

@SpringBootApplicationpublic 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; } }}

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

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

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

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

@RunWith(SpringJUnit4ClassRunner.class)@SpringBootTestpublic 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循環往上面定義的線程池中提交任務,由于是異步執行,在執行過程中,利用System.exit(0)來關閉程序,此時由于有任務在執行,就可以觀察這些異步任務的銷毀與Spring容器中其他資源的順序是否安全。

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

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 omittedCaused 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來看,我們很容易的可以想到,在應用關閉的時候異步任務還在執行,由于Redis連接池先銷毀了,導致異步任務中要訪問Redis的操作就報了上面的錯。所以,我們得出結論,上面的實現方式在應用關閉的時候是不優雅的,那么我們要怎么做呢?

解決方法

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

@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)該方法就是這里的關鍵,用來設置線程池關閉的時候等待所有任務都完成再繼續銷毀其他的Bean,這樣這些異步任務的銷毀就會先于Redis線程池的銷毀。同時,這里還設置了setAwaitTerminationSeconds(60),該方法用來設置線程池中任務的等待時間,如果超過這個時候還沒有銷毀就強制銷毀,以確保應用最后能夠被關閉,而不是阻塞住。

完整示例:

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

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

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

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VeVb武林網的支持。


注:相關教程知識閱讀請移步到JAVA教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美丝袜一区二区三区| 国产精品亚洲视频在线观看| 欧美精品久久久久久久| 欧美色道久久88综合亚洲精品| 国产高清视频一区三区| 91精品国产99| 日韩毛片中文字幕| 精品偷拍各种wc美女嘘嘘| 一个人看的www久久| 欧美日韩国产色视频| 日韩免费视频在线观看| 国产精品扒开腿做爽爽爽的视频| 人九九综合九九宗合| 91国偷自产一区二区三区的观看方式| 成人精品视频在线| 国产一区二中文字幕在线看| 欧美激情精品久久久久久蜜臀| 菠萝蜜影院一区二区免费| 亚洲国产精品久久久久| 精品久久久久人成| 成人免费看片视频| 国内精品小视频| 555www成人网| 国产精品自拍视频| 欧美日韩精品国产| 久久久久久中文| 国产成人亚洲综合91精品| 欧美大胆a视频| 成人激情综合网| 亚洲欧美日韩图片| 亚洲欧洲美洲在线综合| 日韩精品视频中文在线观看| 中文字幕9999| 国产精品丝袜久久久久久不卡| 国产专区精品视频| 欧美激情亚洲一区| 精品亚洲一区二区三区在线播放| 久久精品国产亚洲一区二区| 国产精品一区二区三区在线播放| 欧美日韩中文在线| 国产精品亚发布| 亚洲大胆人体视频| 久久精品在线视频| 国产精自产拍久久久久久| 成人a级免费视频| 日韩免费视频在线观看| 国产精品久久77777| 欧美激情乱人伦| 亚洲欧美日韩成人| 91精品国产成人| 日韩欧美成人精品| 精品无人国产偷自产在线| 91久久国产婷婷一区二区| 亚洲一区二区久久久| 日韩高清av在线| 国产精品高清在线观看| 高清一区二区三区日本久| 国a精品视频大全| 色哟哟入口国产精品| 欧美黄色片视频| 麻豆国产va免费精品高清在线| 亚洲视屏在线播放| 久久精品美女视频网站| 欧美日韩国产专区| 51精品国产黑色丝袜高跟鞋| 国产亚洲免费的视频看| 欧美成人免费一级人片100| 日韩成人中文字幕| 欧美亚洲第一页| 国产成人亚洲综合| 国产91在线高潮白浆在线观看| 久热在线中文字幕色999舞| 91沈先生作品| 91天堂在线观看| 亚洲最新中文字幕| 欧美视频精品一区| 久久视频在线免费观看| 日韩av网站在线| 日本久久精品视频| 亚洲91av视频| 国产精品人成电影在线观看| 欧美一区二三区| 中文字幕v亚洲ⅴv天堂| 亚洲人成电影网站色www| 亚洲日本aⅴ片在线观看香蕉| 久久成人国产精品| 国产精品久久色| 精品视频一区在线视频| 国产精品88a∨| 精品久久久久久国产| 国产精品入口免费视| 亚洲美女精品久久| 91在线免费观看网站| 亚洲最大的成人网| 欧美精品在线观看91| 欧美激情综合亚洲一二区| 欧美一级大片在线免费观看| 亚洲一区二区在线播放| 91香蕉电影院| 欧美成人午夜影院| 日本伊人精品一区二区三区介绍| 日韩在线一区二区三区免费视频| 91精品久久久久久久久久久| 亚洲精品mp4| 国产精品高清在线观看| 久久精品视频免费播放| 7777免费精品视频| 久久人人爽国产| 欧洲成人午夜免费大片| 欧美精品第一页在线播放| 精品国产区一区二区三区在线观看| 国产福利精品av综合导导航| 色综合导航网站| 97香蕉超级碰碰久久免费的优势| 日韩欧美在线中文字幕| 欧洲精品在线视频| 最近2019中文字幕一页二页| 日韩av一区二区在线观看| 色www亚洲国产张柏芝| 成人有码视频在线播放| 久久天天躁狠狠躁夜夜av| 国产九九精品视频| 精品欧美激情精品一区| 国产原创欧美精品| 亚洲免费视频在线观看| 亚洲国产福利在线| 成人性教育视频在线观看| 欧美高清自拍一区| 91禁国产网站| 精品网站999www| 久久免费视频在线观看| www.99久久热国产日韩欧美.com| 日本在线观看天堂男亚洲| 久久精品国产视频| 91久久久在线| 亚洲激情电影中文字幕| 国内精品久久久久| 久久久久久久激情视频| 久久久久久久爱| 日韩视频免费在线| 亚洲综合色av| 久久久亚洲精品视频| 日韩成人av在线| 亚洲精品综合精品自拍| 伊人亚洲福利一区二区三区| 久久久久亚洲精品成人网小说| 动漫精品一区二区| 欧美性生活大片免费观看网址| 7m精品福利视频导航| 777精品视频| 欧美激情亚洲视频| 国产一区二区三区精品久久久| 亚洲精品永久免费| 中文字幕v亚洲ⅴv天堂| 在线观看免费高清视频97| 亚洲bt天天射| 这里只有精品在线观看| 97福利一区二区| 狠狠色狠狠色综合日日五| 久久久国产精品亚洲一区| 欧美最猛性xxxxx(亚洲精品)| 日韩精品视频免费| 亚洲自拍偷拍在线| 欧美成人免费网|