<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>

          為什么阿里巴巴禁止使用 Executors 創(chuàng)建線程池?

          共 832字,需瀏覽 2分鐘

           ·

          2020-12-16 21:17

          點(diǎn)擊上方?程序IT圈選擇?設(shè)為星標(biāo)

          優(yōu)質(zhì)文章,每日送達(dá)


          阿里巴巴開發(fā)手冊(cè)關(guān)于線程池有這樣一條規(guī)定:

          線程池不允許使用 Executors 去創(chuàng)建,而是通過 ThreadPoolExecutor 的方式,這樣的處理方式讓寫的同學(xué)更加明確線程池的運(yùn)行規(guī)則,規(guī)避資源耗盡的風(fēng)險(xiǎn)。

          一、線程池原理

          1.1 為什么使用線程池

          池化技術(shù)的思想主要是為了減少在創(chuàng)建和銷毀線程上所消耗的時(shí)間及系統(tǒng)資源的開銷,解決資源不足的問題。

          1.2 線程池是如何實(shí)現(xiàn)的

          本文只討論通過ThreadPoolExecutor創(chuàng)建的線程池。ThreadPoolExecutor的構(gòu)造器代碼如下,里面涉及到的主要參數(shù)有corePoolSizemaximumPoolSizekeepAliveTimeunitworkQueuethreadFactoryhandler

          public?ThreadPoolExecutor(int?corePoolSize,?
          ??????????????????????????int?maximumPoolSize,
          ??????????????????????????long?keepAliveTime,
          ??????????????????????????TimeUnit?unit,
          ??????????????????????????BlockingQueue?workQueue,
          ??????????????????????????ThreadFactory?threadFactory,
          ??????????????????????????RejectedExecutionHandler?handler)
          ?
          {
          ????if?(corePoolSize?0?||
          ????????maximumPoolSize?<=?0?||
          ????????maximumPoolSize?????????keepAliveTime?0)
          ????????throw?new?IllegalArgumentException();
          ????if?(workQueue?==?null?||?threadFactory?==?null?||?handler?==?null)
          ????????throw?new?NullPointerException();
          ????this.corePoolSize?=?corePoolSize;
          ????this.maximumPoolSize?=?maximumPoolSize;
          ????this.workQueue?=?workQueue;
          ????this.keepAliveTime?=?unit.toNanos(keepAliveTime);
          ????this.threadFactory?=?threadFactory;
          ????this.handler?=?handler;
          }

          這些參數(shù)的含義為:

          1. corePoolSize:核心線程數(shù)
          2. maximumPoolSize:最大線程數(shù)
          3. keepAliveTime:當(dāng)線程池線程數(shù)量大于corePoolSize時(shí)候,多出來的空閑線程的存活時(shí)間
          4. unit:參數(shù)keepAliveTime的時(shí)間單位,TimeUnit枚舉類有小時(shí)毫秒微秒納秒7種可以選擇。
          5. workQueue:線程池使用的緩沖隊(duì)列,可供選擇的有以下幾種。
          參數(shù)描述
          ArrayBlockingQueue一個(gè)由數(shù)組結(jié)構(gòu)組成的有界阻塞隊(duì)列。
          LinkedBlockingQueue一個(gè)由鏈表結(jié)構(gòu)組成的有界阻塞隊(duì)列。常用
          SynchronousQueue一個(gè)不存儲(chǔ)元素的阻塞隊(duì)列,即直接提交給線程不保持它們。常用
          PriorityBlockingQueue一個(gè)支持優(yōu)先級(jí)排序的無界阻塞隊(duì)列。
          DelayQueue一個(gè)使用優(yōu)先級(jí)隊(duì)列實(shí)現(xiàn)的無界阻塞隊(duì)列,只有在延遲期滿時(shí)才能從中提取元素。
          LinkedTransferQueue一個(gè)由鏈表結(jié)構(gòu)組成的無界阻塞隊(duì)列。與SynchronousQueue類似,還含有非阻塞方法。
          LinkedBlockingDeque一個(gè)由鏈表結(jié)構(gòu)組成的雙向阻塞隊(duì)列。
          1. threadFactory:線程工廠,主要用來創(chuàng)建線程
          2. handler:拒絕策略,拒絕處理任務(wù)時(shí)的策略,可供選擇的有以下幾種。
          參數(shù)描述
          AbortPolicy拒絕并拋出異常。默認(rèn)的
          CallerRunsPolicy重試提交當(dāng)前的任務(wù),即再次調(diào)用運(yùn)行該任務(wù)的execute()方法。
          DiscardOldestPolicy拋棄隊(duì)列頭部(最舊)的一個(gè)任務(wù),并執(zhí)行當(dāng)前任務(wù)。
          DiscardPolicy拋棄當(dāng)前任務(wù)。

          1.3 線程池執(zhí)行規(guī)則

          1. 執(zhí)行任務(wù)時(shí),如果線程池中的線程數(shù)量小于corePoolSize,即使池中有空閑的線程數(shù),也會(huì)創(chuàng)建新的線程來執(zhí)行任務(wù)。
          2. 線程池中的線程數(shù)量等于corePoolSize,并且緩沖隊(duì)列未滿時(shí),則任務(wù)被放入緩沖隊(duì)列中
          3. 線程池中的線程數(shù)量大于等于corePoolSize,并且緩沖隊(duì)列已滿,同時(shí)線程數(shù)量小于maximumPoolSize,則會(huì)創(chuàng)建新的線程來執(zhí)行任務(wù)。
          4. 線程池中的線程數(shù)量已滿時(shí),則執(zhí)行拒絕策略處理這些任務(wù)。

          二、阿里巴巴手冊(cè)為什么禁止用Exectors創(chuàng)建線程池

          Exectors提供了幾種工廠方法用來創(chuàng)建線程池,其中newCachedThreadPool()newFixedThreadPool()newSingleThreadExecutor()三種方法最終是通過實(shí)現(xiàn)類ThreadPoolExecutor來創(chuàng)建的。接下來一起看看這三種方法到底有什么問題,為什么阿里巴巴會(huì)禁止使用Exectors來創(chuàng)建線程池!

          2.1 FixedThreadPool 解析

          public?static?ExecutorService?newFixedThreadPool(int?nThreads)?{
          ????return?new?ThreadPoolExecutor(nThreads,?nThreads,
          ??????????????????????????????????0L,?TimeUnit.MILLISECONDS,
          ??????????????????????????????????new?LinkedBlockingQueue());
          }

          具體參數(shù)如下:

          • corePoolSize:nThreads

          • maximumPoolSize:nThreads

          • keepAliveTime:0L

          • unit:毫秒

          • workQueue:LinkedBlockingQueue,一個(gè)由鏈表結(jié)構(gòu)組成的有界阻塞隊(duì)列,并且使用了最大長度的隊(duì)列。

          public?LinkedBlockingQueue()?{
          ????this(Integer.MAX_VALUE);
          }

          這種方式創(chuàng)建的線程池由于核心線程數(shù)和最大線程數(shù)相同,所以線程池中線程的數(shù)量是固定的,并且沒有限制隊(duì)列大小,所以多余的任務(wù)均會(huì)被放到隊(duì)列中排隊(duì),在資源有限時(shí)容易出現(xiàn)內(nèi)存溢出。

          2.2 SingleThreadPool 解析

          public?static?ExecutorService?newSingleThreadExecutor()?{
          ????return?new?FinalizableDelegatedExecutorService
          ????????(new?ThreadPoolExecutor(1,?1,
          ????????????????????????????????0L,?TimeUnit.MILLISECONDS,
          ????????????????????????????????new?LinkedBlockingQueue()));
          }

          具體參數(shù)如下:

          • corePoolSize:1

          • maximumPoolSize:1

          • keepAliveTime:0L

          • unit:毫秒

          • workQueue:LinkedBlockingQueue,一個(gè)由鏈表結(jié)構(gòu)組成的有界阻塞隊(duì)列,并且使用了最大長度的隊(duì)列。

          public?LinkedBlockingQueue()?{
          ????this(Integer.MAX_VALUE);
          }

          這種方式創(chuàng)建的線程池是單線程線程池,核心線程數(shù)和最大線程數(shù)都是1,多余的任務(wù)都將會(huì)被放到緩沖隊(duì)列中去,所以在資源優(yōu)先的情況下容易出現(xiàn)內(nèi)存溢出。

          2.3 CachedThreadPool 解析

          public?static?ExecutorService?newCachedThreadPool()?{
          ????return?new?ThreadPoolExecutor(0,?Integer.MAX_VALUE,
          ??????????????????????????????????60L,?TimeUnit.SECONDS,
          ??????????????????????????????????new?SynchronousQueue());
          }

          具體參數(shù)如下:

          • corePoolSize:0

          • maximumPoolSize:Integer.MAX_VALUE

          • keepAliveTime:60L

          • unit:秒

          • workQueue:SynchronousQueue,一個(gè)不存儲(chǔ)元素的阻塞隊(duì)列,即直接提交給線程不保持它們。

          這種方式創(chuàng)建的線程池核心線程數(shù)為0,并且使用了SynchronousQueue隊(duì)列,這個(gè)隊(duì)列不存儲(chǔ)元素,也就是任務(wù)直接會(huì)直接通過創(chuàng)建非核心線程來執(zhí)行,核心線程數(shù)為Integer.MAX_VALUE,可以任務(wù)能無限創(chuàng)建隊(duì)列,因此在資源優(yōu)先的情況下容易發(fā)生內(nèi)存溢出。

          2.4 測試OOM異常

          既然我們已經(jīng)分析了三種創(chuàng)建線程池可能會(huì)出現(xiàn)OOM異常,那么我們測試一下到底會(huì)不會(huì)發(fā)生OOM呢?這里我將選擇newSingleThreadExecutor()來進(jìn)行測試,其他兩個(gè)方法測試流程也是一樣的。為了盡快出現(xiàn)OOM,我們將JVM的內(nèi)存調(diào)小一點(diǎn)。

          • -Xmx5M :最大內(nèi)存值5M
          • -Xms5M:初始內(nèi)存大小5M

          測試代碼

          public?static?void?main(String[]?args)?{

          ????ExecutorService?service?=?Executors.newSingleThreadExecutor();
          ????while?(true){
          ????????service.execute(()?->?{
          ????????????System.out.println("我是一個(gè)任務(wù),運(yùn)行時(shí)間:"+System.currentTimeMillis()+"\n");
          ????????});
          ????}
          }

          測試結(jié)果

          任務(wù)跑了1分鐘左右,就發(fā)生了OOM異常

          三、總結(jié)

          阿里巴巴開發(fā)手冊(cè)為什么禁止使用 Executors 去創(chuàng)建線程池,原因就是 newFixedThreadPool()newSingleThreadExecutor()兩個(gè)方法允許請(qǐng)求的最大隊(duì)列長度是 Integer.MAX_VALUE ,可能會(huì)出現(xiàn)任務(wù)堆積,出現(xiàn)OOM。newCachedThreadPool()允許創(chuàng)建的線程數(shù)量為 Integer.MAX_VALUE,可能會(huì)創(chuàng)建大量的線程,導(dǎo)致發(fā)生OOM。它建議使用ThreadPoolExecutor方式去創(chuàng)建線程池,通過上面的分析我們也知道了其實(shí)Executors 三種創(chuàng)建線程池的方式最終就是通過ThreadPoolExecutor來創(chuàng)建的,只不過有些參數(shù)我們無法控制,如果通過ThreadPoolExecutor的構(gòu)造器去創(chuàng)建,我們就可以根據(jù)實(shí)際需求控制線程池需要的任何參數(shù),避免發(fā)生OOM異常。


          < END >


          往期精選:


          10張圖讓你徹底理解回調(diào)函數(shù)

          MySQL大表優(yōu)化方案

          Redis 為什么默認(rèn) 16 個(gè)數(shù)據(jù)庫?


          在這里獲得的不僅僅是技術(shù)!


          喜歡就給個(gè)“在看


          瀏覽 56
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  国产黄色电影一区 | 熟女综合 | 超碰人妻操 | 三级成人导航 | 女人十八岁毛片 |