日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
被開(kāi)發(fā)者拋棄的Executors,錯(cuò)在哪兒?

一、序

在 Java 領(lǐng)域內(nèi),我們使用多線程的方式來(lái)實(shí)現(xiàn)并發(fā)編程。而線程本身是操作系統(tǒng)的一個(gè)概念,雖然不同的語(yǔ)言對(duì)線程都進(jìn)行了一些封裝,但是最終都是調(diào)用到操作系統(tǒng)中去創(chuàng)建和調(diào)度線程。

既然線程是一項(xiàng)重要的系統(tǒng)資源,為了更合理的利用此資源,我們會(huì)使用池化技術(shù)來(lái)優(yōu)化線程的創(chuàng)建和銷毀,這就是線程池。

在我們學(xué)習(xí)并發(fā)編程的時(shí)候,線程可以利用 Thread 來(lái)創(chuàng)建并通過(guò) start() 來(lái)啟動(dòng)一個(gè)線,但在成熟的項(xiàng)目中,基本上是不允許這樣操作線程的,都需要通過(guò)線程池去收斂線程的使用,所以線程池是必須的。

Java 的線程池可以通過(guò) ThreadPoolExecutor 來(lái)構(gòu)造,在其中提供非常完備的構(gòu)造方法,可以根據(jù)我們的業(yè)務(wù)需求靈活的構(gòu)造線程池。同時(shí) Java 還提供了一個(gè) Executors,它內(nèi)部提供了很多包裝的方法,利用它可以幫我們快速的構(gòu)建線程池。

原本 Executors 的目的就是為了讓我們更方便的使用線程池,但是《阿里巴巴Java開(kāi)發(fā)手冊(cè)》也明確指出,直接使用 Executors 的缺陷。

手冊(cè)中提到強(qiáng)制不允許使用 Executors 去創(chuàng)建線程池,而是應(yīng)該使用退化到最原始的 ThreadPoolExecutor 的方式。

日常開(kāi)發(fā)中,應(yīng)該收緊對(duì)線程池的創(chuàng)建,由開(kāi)發(fā)人員明確線程池的運(yùn)行規(guī)則,以此來(lái)盡量規(guī)避其資源耗盡的風(fēng)險(xiǎn)。

線程池是個(gè)好東西,但是怎么創(chuàng)建是一個(gè)問(wèn)題。

二、Executors 怎么了?

1. 不被允許的 Executors

不應(yīng)該使用 Executors 的原因,其實(shí)《阿里巴巴Java開(kāi)發(fā)手冊(cè)》里已經(jīng)寫明了,當(dāng)需要處理大量任務(wù)的時(shí)候,可能會(huì)出現(xiàn) OOM 異常,但它們出現(xiàn) OOM 的原因并不一樣。

ThreadPoolExecutor 的構(gòu)造方法中,提供了很多參數(shù)的配置,其中與 Executors 出現(xiàn) OOM 相關(guān)的就有 2 個(gè):核心線程數(shù)和等待隊(duì)列。

先來(lái)看看 FixedThreadPool 和 SingleThreadPool 出現(xiàn) OOM 的原因。

它們的問(wèn)題在于等待隊(duì)列使用了 LinkedBlockingQueue 這個(gè)以鏈表實(shí)現(xiàn)的無(wú)界隊(duì)列(最大長(zhǎng)度是 Integer.MAX_VALUE),最終導(dǎo)致堆積了大量等待處理的任務(wù),從而導(dǎo)致頻繁的 GC,最終觸發(fā) OOM。

 
 
 
 
  1. java.lang.OutOfMemoryError: GC overhead limit exceeded 

再來(lái)看看 CachedThreadPool 出現(xiàn) OOM 的原因。

它的問(wèn)題在于核心線程數(shù)設(shè)置為了 Integer.MAX_VALUE,并且等待隊(duì)列是一個(gè) SynchronousQueue。

SynchronousQueue 是一個(gè)沒(méi)有數(shù)據(jù)緩沖的阻塞隊(duì)列,它極易被阻塞。在等待隊(duì)列被阻塞的時(shí)候,如果線程數(shù)量還沒(méi)有達(dá)到核心線程數(shù)限制的數(shù)量時(shí),線程池的策略是創(chuàng)建新的線程來(lái)處理新的任務(wù)。

也就是說(shuō),是核心線程數(shù)和等待隊(duì)列 SynchronousQueue 合力造成了線程會(huì)跟隨任務(wù)不斷的被創(chuàng)建,直到觸發(fā) OOM。

 
 
 
 
  1. java.lang.OutOfMemoryError: pthread_creat (1040KB stack) failed: Try again 

ScheduledThreadPool 的等待隊(duì)列使用的是 DelayedWorkQueue,原理也是類似的,最終會(huì)導(dǎo)致創(chuàng)建大量的線程而拋出 OOM。

線程是一種系統(tǒng)資源,本身創(chuàng)建就會(huì)帶來(lái)內(nèi)存開(kāi)銷,同時(shí)操作系統(tǒng)對(duì)單進(jìn)程可創(chuàng)建的線程數(shù)也是有限制的。

在 Android 中,每個(gè)線程初始化都需要 mmap 一定的堆內(nèi)存,在默認(rèn)的情況下,初始化一個(gè)線程大約需要 mmap 1MB 左右的內(nèi)存空間。同時(shí)系統(tǒng)本身也會(huì)對(duì)每個(gè)進(jìn)程可創(chuàng)建的線程數(shù),做一定的限制,這個(gè)限制在 /proc/pid/limits 中,不同的廠商對(duì)這個(gè)限制也有所不同,當(dāng)超出限制時(shí),哪怕堆上還有可用內(nèi)存,依然會(huì)拋出 OOM。

2. Executors 錯(cuò)在哪兒了?

Executors 會(huì)在任務(wù)過(guò)多的時(shí)候,導(dǎo)致資源耗盡而觸發(fā) OOM,這是它帶來(lái)的危害。

Executors 最大的問(wèn)題,在于沒(méi)有邊界。

在系統(tǒng)環(huán)境良好,任務(wù)不多的時(shí)候 Executors 創(chuàng)建的線程池,都是可以正常工作的。

但是一旦有重壓,我們就無(wú)法預(yù)知什么時(shí)候會(huì)出現(xiàn)問(wèn)題,這就是沒(méi)有邊界,沒(méi)有邊界就意味著不可控。

我們很難去信任一段不可控的代碼,它什么時(shí)候出現(xiàn)問(wèn)題,完全是不可預(yù)知的,這才是 Executors 最大的問(wèn)題。

除此之外,Executors 封裝了太多線程池的細(xì)節(jié),本身也不建議使用。例如通常我們需要給線程池創(chuàng)建的線程,起一個(gè)有意義的名稱,方便在出現(xiàn)異常的時(shí)候排查問(wèn)題;再例如對(duì)與線程池的拒絕策略,我們需要深思熟慮的定義,是直接拋棄還是持久化下來(lái)延遲處理。

去思考一個(gè)線程池的不同參數(shù)帶來(lái)的策略細(xì)節(jié),才是使用線程池的一個(gè)良好的開(kāi)發(fā)習(xí)慣。

三. 小結(jié)時(shí)刻

本文我們聊了關(guān)于創(chuàng)建線程池,使用 Executors 創(chuàng)建的線程池會(huì)有 OOM 的風(fēng)險(xiǎn),應(yīng)該使用 ThreadPoolExecutor 去創(chuàng)建線程池。通過(guò)思考業(yè)務(wù)來(lái)明確配置線程池不同的參數(shù),例如線程池、等待隊(duì)列、拒絕策略等等。


分享名稱:被開(kāi)發(fā)者拋棄的Executors,錯(cuò)在哪兒?
本文網(wǎng)址:http://www.5511xx.com/article/djpdpoj.html