<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          新來了個技術(shù)總監(jiān):誰再用 @Async 創(chuàng)建線程以后就不用來了!!

          共 2962字,需瀏覽 6分鐘

           ·

          2022-03-23 12:09

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

          原因無它,有小伙伴開啟了線程池的暴力使用模式。

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

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

          我一直認(rèn)為這是一個非常偶發(fā)的低級錯誤,發(fā)生頻率非常的低。但隨著這樣的故障越來越多,我認(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注解,因?yàn)檫@種涉及到多線程的東西,即使是自己去創(chuàng)建線程,也是心懷敬畏,唯恐?jǐn)_了操作系統(tǒng)的安寧。@Async這樣的黑盒,真的可以那么順暢的使用么?

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

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

          @SpringBootApplication
          @EnableAsync
          public?class?DemoApplication?{

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

          @Component
          public?class?AsyncService?{
          ????@Async
          ????public?void?async(){
          ????????try?{
          ????????????Thread.sleep(1000);
          ????????????System.out.println(Thread.currentThread());
          ????????}catch?(Exception?ex){
          ????????????ex.printStackTrace();
          ????????}
          ????}
          }

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

          @ResponseBody
          @GetMapping("test")
          public?void?test(){
          ????service.async();
          }

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

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

          throw?new?TaskRejectedException("Executor?["?+?executor?+?"]?did?not?accept?task:?"?+?task,?var4);

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

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

          2.是SpringBoot救了你

          SpringBoot是個好東西。

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

          @ConditionalOnMissingBean({Executor.class})
          public?ThreadPoolTaskExecutor?applicationTaskExecutor(TaskExecutorBuilder?builder)?{
          ????return?builder.build();
          }

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

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

          @Override
          @Nullable
          protected?Executor?getDefaultExecutor(@Nullable?BeanFactory?beanFactory)?{
          ?Executor?defaultExecutor?=?super.getDefaultExecutor(beanFactory);
          ?return?(defaultExecutor?!=?null???defaultExecutor?:?new?SimpleAsyncTaskExecutor());
          }

          這就是Spring大仙所干的事。

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

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

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

          3.End

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

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

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

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

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

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

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


          沒有什么使我停留——除了目的,縱然岸旁有玫瑰、有綠蔭、有寧靜的港灣,我是不系之舟

          推薦閱讀

          瀏覽 28
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美内射视频网站 | 国产精品一色 | 国产美女被艹 | 东北骚妇大战黑人视频 | 开心五月天激情成人网 |