0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

強(qiáng)烈反對使用Spring封裝的多線程類

PLC工控專欄 ? 來源:加密狗破解 ? 作者:加密狗破解 ? 2022-02-22 08:58 ? 次閱讀

在很久很久之前,我有一段痛苦的記憶。那種被故障所驅(qū)使的感覺,在我腦海里久久無法驅(qū)散。

原因無它,有小伙伴開啟了線程池的暴力使用模式。沒錯,就是下面這篇文章。

奪命故障 ! 炸出了投資人!

我有必要簡單的復(fù)述一下。其主要原因,就是開發(fā)人員,在每一次方法調(diào)用里,都創(chuàng)建了一個單獨的線程池去處理。這樣的話,如果請求量一增加,整個操作系統(tǒng)的壓力就會耗盡,最終所有的業(yè)務(wù)都無法響應(yīng)。

強(qiáng)烈反對使用Spring封裝的多線程類

我一直認(rèn)為這是一個非常偶發(fā)的低級錯誤,發(fā)生頻率非常的低。但隨著這樣的故障越來越多,xjjdog認(rèn)識到這是一個普遍的現(xiàn)象。

以異步性能優(yōu)化為目的,反而帶來的整體業(yè)務(wù)不可用的結(jié)果,是非常打臉的一種優(yōu)化。

1.Spring的異步代碼

Spring作為Java屆的杠把子框架,其過度封裝的API深得開發(fā)人員的喜愛。根據(jù)語義化編程的邏輯,只要某些關(guān)鍵字在語言層面上過得去,我們就可以把它給加上去。比如@Async注解。

我永遠(yuǎn)想不通是什么給了開發(fā)人員勇氣,去加上這個@Async注解,因為這種涉及到多線程的東西,即使是自己去創(chuàng)建線程,也是心懷敬畏,唯恐?jǐn)_了操作系統(tǒng)的安寧。@Async這樣的黑盒,真的可以那么順暢的使用么?

我們不妨debug一下代碼,讓子彈飛一會兒。

首先,生成一個小小的項目,然后在主類上加上必須的注解。嗯,別忘了這一環(huán),否則你后面加的注解將沒什么用處。

@SpringBootApplication
@EnableAsync
publicclassDemoApplication{

復(fù)制代碼

創(chuàng)造一個帶@Async注解的方法。

@Component
publicclassAsyncService{
@Async
publicvoidasync(){
try{
Thread.sleep(1000);
System.out.println(Thread.currentThread());
}catch(Exceptionex){
ex.printStackTrace();
}
}
}

復(fù)制代碼

然后,做一個對應(yīng)的test接口,訪問時會調(diào)用這個async方法。

@ResponseBody
@GetMapping("test")
publicvoidtest(){
service.async();
}

復(fù)制代碼

訪問時,直接打個斷點,即可獲取執(zhí)行異步線程的線程池。

強(qiáng)烈反對使用Spring封裝的多線程類

可以看到,異步任務(wù)使用了一個線程池,它的corePoolSize=8, 阻塞隊列采用了無界隊列LinkedBlockingQueue。一旦采用了這樣的組合,最大線程數(shù)就會形同虛設(shè),因為超出8個線程的任務(wù),將全部會被放到無界隊列里。使得下面的代碼變成了擺設(shè)。

thrownewTaskRejectedException("Executor["+executor+"]didnotaccepttask:"+task,var4);

復(fù)制代碼

如果你的訪問量非常大,這些任務(wù)將全部堆積在LinkedBlockingQueue里。情況好一點的,這些任務(wù)的執(zhí)行會變得延遲很大;情況壞一點的,任務(wù)太多將直接造成內(nèi)存溢出OOM!

你可能會說,我可以自己指定另外一個ThreadPoolExceute,然后使用@Async注解來聲明啊。說這話的同學(xué),一定是能力比較強(qiáng),或者Review的代碼比較少,沒有經(jīng)過豬隊友的洗禮。

2.是SpringBoot救了你

SpringBoot是個好東西。

在TaskExecutionAutoConfiguration中,通過生成ThreadPoolTaskExecutor的Bean,來提供默認(rèn)的Executor。

@ConditionalOnMissingBean({Executor.class})
publicThreadPoolTaskExecutorapplicationTaskExecutor(TaskExecutorBuilderbuilder){
returnbuilder.build();
}

復(fù)制代碼

也就是我們上面所說的那個。如果沒有SpringBoot的助力,Spring將默認(rèn)使用SimpleAsyncTaskExecutor。

參見org.springframework.aop.interceptor.AsyncExecutionInterceptor。

@Override
@Nullable
protectedExecutorgetDefaultExecutor(@NullableBeanFactorybeanFactory){
ExecutordefaultExecutor=super.getDefaultExecutor(beanFactory);
return(defaultExecutor!=null?defaultExecutor:newSimpleAsyncTaskExecutor());
}

復(fù)制代碼

這就是Spring大仙所干的事。

SimpleAsyncTaskExecutor類設(shè)計的非常操蛋,因為它每執(zhí)行一次,都會創(chuàng)建一個單獨的線程,根本沒有共用線程池。比如你的TPS是1000,異步執(zhí)行了任務(wù),那么你每秒將會生成1000個線程!

這明顯是想要累死操作系統(tǒng)的節(jié)奏。

protectedvoiddoExecute(Runnabletask){
Threadthread=(this.threadFactory!=null?this.threadFactory.newThread(task):createThread(task));
thread.start();
}

復(fù)制代碼

3.End

明眼人一看,這種使用new線程的處理方式將會是非常可怕的。但就拿Spring本身來說,引用SimpleAsyncTaskExecutor這個類的地方還不少,包括比較流行的AsyncRestTemplate。

強(qiáng)烈反對使用Spring封裝的多線程類

這暴露了很多風(fēng)險,尤其是竟然在這些列表中看到了redis的身影。這個類的設(shè)計,使得任務(wù)的執(zhí)行變的非常的不可控。

看這個API,我感覺Spring是進(jìn)入了設(shè)計的魔怔狀態(tài)。

這個東西的隱藏bug可能還會更深!比如org.springframework.context.event.EventListener注解,用于實現(xiàn)DDD那套所謂的事件驅(qū)動模式,有不少框架直接set了AsyncRestTemplate,那么就等死吧。

趕緊把SimpleAsyncTaskExecutor加入你的API黑名單,或者埋坑清單吧!

創(chuàng)建線程有那么難么?需要使用Spring創(chuàng)建的線程?有時候我實在是想不通,暴露出這樣的接口目的是為了什么。

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 封裝
    +關(guān)注

    關(guān)注

    125

    文章

    7598

    瀏覽量

    142158
  • 多線程
    +關(guān)注

    關(guān)注

    0

    文章

    275

    瀏覽量

    19850
  • spring
    +關(guān)注

    關(guān)注

    0

    文章

    335

    瀏覽量

    14259
收藏 人收藏

    評論

    相關(guān)推薦

    LWIP多線程強(qiáng)烈建議開啟LWIP_ASSERT_CORE_LOCKED宏,這個在RTT里面要怎么實現(xiàn)?

    LWIP多線程強(qiáng)烈建議開啟LWIP_ASSERT_CORE_LOCKED宏,這個在RTT里面要怎么實現(xiàn),之前參考網(wǎng)上代碼,這樣寫,壓力測試下有概率斷言失敗 extern sys_mutex_t
    發(fā)表于 07-25 06:27

    多線程設(shè)計模式到對 CompletableFuture 的應(yīng)用

    最近在開發(fā) 延保服務(wù) 頻道頁時,為了提高查詢效率,使用到了多線程技術(shù)。為了對多線程方案設(shè)計有更加充分的了解,在業(yè)余時間讀完了《圖解 Java 多線程設(shè)計模式》這本書,覺得收獲良多。本篇文章將介紹其中
    的頭像 發(fā)表于 06-26 14:18 ?181次閱讀
    從<b class='flag-5'>多線程</b>設(shè)計模式到對 CompletableFuture 的應(yīng)用

    java實現(xiàn)多線程的幾種方式

    了多種實現(xiàn)多線程的方式,本文將詳細(xì)介紹以下幾種方式: 1.繼承Thread 2.實現(xiàn)Runnable接口 3.Callable和Future 4.線程池 5.Java 8中
    的頭像 發(fā)表于 03-14 16:55 ?443次閱讀

    AT socket可以多線程調(diào)用嗎?

    請問AT socket 可以多線程調(diào)用嗎? 有互鎖機(jī)制嗎,還是要自己做互鎖。
    發(fā)表于 03-01 08:22

    redis多線程還能保證線程安全嗎

    是單線程的,多個客戶端請求會按序執(zhí)行,每個請求使用一個線程完成,這樣可以避免多線程之間的競爭條件和鎖等帶來的開銷。但是,由于Redis是存儲內(nèi)存中的數(shù)據(jù)的,當(dāng)多個客戶端同時對同一個數(shù)據(jù)進(jìn)行讀寫操作時,就會存在
    的頭像 發(fā)表于 12-05 10:28 ?1360次閱讀

    mfc多線程編程實例

    (圖形用戶界面)應(yīng)用程序的開發(fā)。在這篇文章中,我們將重點介紹MFC中的多線程編程。 多線程編程在軟件開發(fā)中非常重要,它可以實現(xiàn)程序的并發(fā)執(zhí)行,提高程序的效率和響應(yīng)速度。MFC提供了豐富的多線程支持,可以輕松地實現(xiàn)
    的頭像 發(fā)表于 12-01 14:29 ?1161次閱讀

    Spring Boot 3.2支持虛擬線程和原生鏡像

    Spring Boot 3.2 前幾日發(fā)布,讓我們用 Java 21、GraalVM 和虛擬線程來嘗試一下。
    的頭像 發(fā)表于 11-30 16:22 ?588次閱讀

    多線程如何保證數(shù)據(jù)的同步

    多線程編程是一種并發(fā)編程的方法,意味著程序中同時運行多個線程,每個線程可獨立執(zhí)行不同的任務(wù),共享同一份數(shù)據(jù)。由于多線程并發(fā)執(zhí)行的特點,會引發(fā)數(shù)據(jù)同步的問題,即保證多個
    的頭像 發(fā)表于 11-17 14:22 ?911次閱讀

    多線程同步的幾種方法

    多線程同步是指在多個線程并發(fā)執(zhí)行的情況下,為了保證線程執(zhí)行的正確性和一致性,需要采用特定的方法來協(xié)調(diào)線程之間的執(zhí)行順序和共享資源的訪問。下面將介紹幾種常見的
    的頭像 發(fā)表于 11-17 14:16 ?969次閱讀

    Linux系統(tǒng)上多線程和多進(jìn)程的運行效率

    關(guān)于多進(jìn)程和多線程,教科書上最經(jīng)典的一句話是“進(jìn)程是資源分配的最小單位,線程是CPU調(diào)度的最小單位”,這句話應(yīng)付考試基本上夠了,但如果在工作中遇到類似的選擇問題,那就沒有這么簡單了,選的不好,會讓你
    的頭像 發(fā)表于 11-10 10:54 ?1067次閱讀
    Linux系統(tǒng)上<b class='flag-5'>多線程</b>和多進(jìn)程的運行效率

    關(guān)于Python多進(jìn)程和多線程詳解

    進(jìn)程(process)和線程(thread)是操作系統(tǒng)的基本概念,但是它們比較抽象,不容易掌握。關(guān)于多進(jìn)程和多線程,教科書上最經(jīng)典的一句話是“進(jìn)程是資源分配的最小單位,線程是CPU調(diào)度的最小單位”。
    的頭像 發(fā)表于 11-06 14:46 ?695次閱讀
    關(guān)于Python多進(jìn)程和<b class='flag-5'>多線程</b>詳解

    多線程idm下載軟件

    多線程idm下載軟件
    發(fā)表于 10-23 09:23 ?0次下載

    Spring線程池應(yīng)用

    我們在日常開發(fā)中,經(jīng)常跟多線程打交道,Spring 為我們提供了一個線程池方便我們開發(fā),它就是 ThreadPoolTaskExecutor ,接下來我們就來聊聊 Spring
    的頭像 發(fā)表于 10-13 10:47 ?527次閱讀
    <b class='flag-5'>Spring</b> 的<b class='flag-5'>線程</b>池應(yīng)用

    MDK可以支持多線程編譯嗎?

    怎么才能打開多線程編譯
    發(fā)表于 10-11 07:23

    Java多線程的用法

    本文將介紹一下Java多線程的用法。 基礎(chǔ)介紹 什么是多線程 指的是在一個進(jìn)程中同時運行多個線程,每個線程都可以獨立執(zhí)行不同的任務(wù)或操作。 與單線程
    的頭像 發(fā)表于 09-30 17:07 ?848次閱讀