精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

創建線程有那么難么?創建線程的大坑要避開

jf_ro2CN3Fa ? 來源:芋道源碼 ? 作者:芋道源碼 ? 2022-10-19 10:36 ? 次閱讀

1.Spring的異步代碼

2.是SpringBoot救了你

3.End

e59430cc-4c39-11ed-a3b6-dac502259ad0.jpg

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

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

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

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

e5a5c77e-4c39-11ed-a3b6-dac502259ad0.png

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

以異步性能優化為目的,反而帶來的整體業務不可用的結果,是非常打臉的一種優化。

1.Spring的異步代碼

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

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

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

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

@SpringBootApplication
@EnableAsync
publicclassDemoApplication{

創造一個帶@Async注解的方法。

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

然后,做一個對應的test接口,訪問時會調用這個async方法。

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

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

e5b246ca-4c39-11ed-a3b6-dac502259ad0.png

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

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

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

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

2.是SpringBoot救了你

SpringBoot是個好東西。

在TaskExecutionAutoConfiguration中,通過生成ThreadPoolTaskExecutor的Bean,來提供默認的Executor。

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

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

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

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

這就是Spring大仙所干的事。

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

這明顯是想要累死操作系統的節奏。

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

3.End

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

e5c56796-4c39-11ed-a3b6-dac502259ad0.png

這暴露了很多風險,尤其是竟然在這些列表中看到了redis的身影。這個類的設計,使得任務的執行變的非常的不可控。

看這個API,我感覺Spring是進入了設計的魔怔狀態。

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

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

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

就連原生的線程池我們還沒搞明白呢,你還給包了一層,這是方便我們甩鍋啊!

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • JAVA
    +關注

    關注

    19

    文章

    2959

    瀏覽量

    104553
  • API
    API
    +關注

    關注

    2

    文章

    1486

    瀏覽量

    61819
  • 代碼
    +關注

    關注

    30

    文章

    4750

    瀏覽量

    68357
  • spring
    +關注

    關注

    0

    文章

    338

    瀏覽量

    14311
  • 線程
    +關注

    關注

    0

    文章

    504

    瀏覽量

    19651

原文標題:新來了個技術總監:誰再用 @Async 創建線程以后就不用來了!!

文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    跨平臺的線程池組件--TP組件

    /銷毀代價是很高的。那么我們怎么去設計多線程編程呢???答案:對于長駐的線程,我們可以創建獨立的線程
    的頭像 發表于 04-06 15:39 ?850次閱讀

    fx3線程創建

    你好,如果我使用堆棧大小0x400(1024)使用Cyu3pthRead CueRead()API創建線程那么我可以使用Cyu3PDMACHNEL CURATE()API創建多少個通
    發表于 10-16 07:52

    如何在psoc cy8kit-059中創建線程

    你好,我想在PSoC CY8KIT-059中創建一個線程。我的Linux使用pthread創建一個線程但我不怎么在PSoC創建
    發表于 11-05 08:54

    初學RT-thread線程動態創建

    RT-thread初學線程動態創建線程靜態創建線程鉤子函數定時器獲取系統時間動態創建定時器靜態
    發表于 02-24 07:32

    python創建線程的兩種方法

    ()可以看到輸出hello Pythonhello MINGhello Pythonhello MING2. 用類創建線程相比較函數而言,使用類創建線程,會比較麻煩一點。首先,我們
    發表于 03-15 16:47

    請問大佬rt-thread smart用戶程序可以創建實時線程?

    RT,rt-smart用戶程序可以創建實時線程?現有例子看,感覺smart得內核是實時得,用戶層程序是非實時得呢?如果用戶可以創建實時線程
    發表于 04-15 09:54

    在RT-Thread系統中創建線程哪幾種方式

    概述創建線程三要素:1.線程棧2.線程控制塊3.線程主體函數在RTT中線程
    發表于 05-07 14:14

    RTThread創建線程時棧空間是如何進行分配的

    RTThread在動態創建線程時,其中一個參數是分配的線程堆棧大小。單個線程的堆棧大小可以在rtconfig文件中定義,那么總的堆棧空間是由
    發表于 09-21 10:57

    怎么解決創建多個線程和消息隊列失敗的問題呢?

    我使用的是STM32L010RB,在Main函數中創建了4個線程線程棧大小分別為256\\\\1024\\\\1024\\\\1024,結果在后面的線程中提示
    發表于 05-05 14:20

    linux下多線程創建與等待詳解

    帶來的問題回避進程嚴重得多。如果你的主線程,也就是main函數執行的那個線程,在你其他線程推出之前就已經退出,那么帶來的bug則不可估量。
    發表于 04-02 14:48 ?315次閱讀

    python創建線程的兩種方法

    1. 用函數創建線程 在Python3中,Python提供了一個內置模塊 threading.Thread ,可以很方便地讓我們創建線程。 threading.Thread() 一
    的頭像 發表于 03-15 16:47 ?5273次閱讀

    python創建線程池的兩種方法

    在使用多線程處理任務時也不是線程越多越好,由于在切換線程的時候,需要切換上下文環境,依然會造成cpu的大量開銷。為解決這個問題,線程池的概念被提出來了。預先
    的頭像 發表于 03-16 16:15 ?5960次閱讀

    stm32裸機RT—thread開始創建線程詳解

    線程系統中,每個線程都是獨立的,互不干擾的,所以要為每個線程都分配獨立的棧空間,這個棧空間通常是一個預先定義好的全局數組,也可以是動態分配的一段內存空間,但它們都存在于RAM中。 ? ? ? ?
    的頭像 發表于 05-19 15:02 ?4907次閱讀
    stm32裸機RT—thread開始<b class='flag-5'>創建</b><b class='flag-5'>線程</b>詳解

    linux內核線程就這樣誕生了

    線程是操作系統的重要組成部件之一,linux內核中,內核線程是如何創建的,在內核啟動過程中,誕生了哪些支撐整個系統運轉的線程,本文將帶著這個疑問瞅一瞅內核源碼,分析內核
    的頭像 發表于 07-10 10:45 ?753次閱讀
    linux內核<b class='flag-5'>線程</b>就這樣誕生了<b class='flag-5'>么</b>?

    線程池的創建方式幾種

    的開銷。線程池的創建方式多種,下面將詳細介紹幾種常用的線程創建方式。 手動創建
    的頭像 發表于 12-04 16:52 ?827次閱讀