您好,登錄后才能下訂單哦!
本文小編為大家詳細(xì)介紹“反對使用Spring封裝的多線程類原因是什么”,內(nèi)容詳細(xì),步驟清晰,細(xì)節(jié)處理妥當(dāng),希望這篇“反對使用Spring封裝的多線程類原因是什么”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學(xué)習(xí)新知識吧。
前言:
工作總難免會遇到被故障所驅(qū)使,其實是開啟了線程池的暴力使用模式
我有必要簡單的復(fù)述一下。其主要原因,就是開發(fā)人員,在每一次方法調(diào)用里,都創(chuàng)建了一個單獨的線程池去處理。這樣的話,如果請求量一增加,整個操作系統(tǒng)的壓力就會耗盡,最終所有的業(yè)務(wù)都無法響應(yīng)。
我一直認(rèn)為這是一個非常偶發(fā)的低級錯誤,發(fā)生頻率非常的低。但隨著這樣的故障越來越多,xjjdog認(rèn)識到這是一個普遍的現(xiàn)象。
以異步性能優(yōu)化為目的,反而帶來的整體業(yè)務(wù)不可用的結(jié)果,是非常打臉的一種優(yōu)化。
Spring作為Java屆的杠把子框架,其過度封裝的API深得開發(fā)人員的喜愛。根據(jù)語義化編程的邏輯,只要某些關(guān)鍵字在語言層面上過得去,我們就可以把它給加上去。比如@Async
注解。
我永遠(yuǎn)想不通是什么給了開發(fā)人員勇氣,去加上這個@Async
注解,因為這種涉及到多線程的東西,即使是自己去創(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(); }
訪問時,直接打個斷點,即可獲取執(zhí)行異步線程的線程池。
可以看到,異步任務(wù)使用了一個線程池,它的corePoolSize=8, 阻塞隊列采用了無界隊列LinkedBlockingQueue。一旦采用了這樣的組合,最大線程數(shù)就會形同虛設(shè),因為超出8個線程的任務(wù),將全部會被放到無界隊列里。使得下面的代碼變成了擺設(shè)。
throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, var4);
如果你的訪問量非常大,這些任務(wù)將全部堆積在LinkedBlockingQueue里。情況好一點的,這些任務(wù)的執(zhí)行會變得延遲很大;情況壞一點的,任務(wù)太多將直接造成內(nèi)存溢出OOM!
你可能會說,我可以自己指定另外一個ThreadPoolExceute,然后使用@Async注解來聲明啊。說這話的同學(xué),一定是能力比較強(qiáng),或者Review的代碼比較少,沒有經(jīng)過豬隊友的洗禮。
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è)計的非常操蛋,因為它每執(zhí)行一次,都會創(chuàng)建一個單獨的線程,根本沒有共用線程池。比如你的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(); }
明眼人一看,這種使用new線程的處理方式將會是非??膳碌摹5湍肧pring本身來說,引用SimpleAsyncTaskExecutor這個類的地方還不少,包括比較流行的AsyncRestTemplate。
這暴露了很多風(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)建的線程?有時候我實在是想不通,暴露出這樣的接口目的是為了什么。
讀到這里,這篇“反對使用Spring封裝的多線程類原因是什么”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領(lǐng)會,如果想了解更多相關(guān)內(nèi)容的文章,歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。