<ul id="qxxfc"><fieldset id="qxxfc"><tr id="qxxfc"></tr></fieldset></ul>


      帶著問題

      * 阿里Java代碼規(guī)范為什么不允許使用Executors快速創(chuàng)建線程池?
      * 下面的代碼輸出是什么? ThreadPoolExecutor executor = new ThreadPoolExecutor( 1,
      //corePoolSize 100, //maximumPoolSize 100, //keepAliveTime TimeUnit.SECONDS,
      //unit new LinkedBlockingDeque<>(100));//workQueue for (int i = 0; i < 5; i++)
      { final int taskIndex = i; executor.execute(() -> {
      System.out.println(taskIndex); try { Thread.sleep(Long.MAX_VALUE); } catch
      (InterruptedException e) { e.printStackTrace(); } }); }
      A) 0 1 2 3 4 5

      B) 0~5 順序不一致輸出5行

      C) 0

      基礎(chǔ)

      什么是線程池?

      線程池可以通過池看出來是一個(gè)資源集,任何池的作用都大同小異,主要是用來減少資源創(chuàng)建、初始化的系統(tǒng)開銷。

      創(chuàng)建線程很“貴”嗎?

      是的。創(chuàng)建線程的代價(jià)是昂貴的。

      我們都知道系統(tǒng)中的每個(gè)進(jìn)程有自己獨(dú)立的內(nèi)存空間,而被稱為輕量級進(jìn)程的線程也是需要的。


      在JVM中默認(rèn)一個(gè)線程需要使用256k~1M(取決于32位還是64位操作系統(tǒng))的內(nèi)存。(具體的數(shù)組我們不深究,因?yàn)殡S著JVM版本的變化這個(gè)默認(rèn)值隨時(shí)可能發(fā)生變更,我們只需要知道線程是需要占用內(nèi)存的)

      除了內(nèi)存還有更多嗎?
      許多文章會將上下文切換、CPU調(diào)度列入其中,這邊不將線程調(diào)度列入是因?yàn)樗咧械木€程不會被調(diào)度(OS控制),如果不是睡眠中的線程那么是一定需要被調(diào)度的。
      但在JVM中除了創(chuàng)建時(shí)的內(nèi)存消耗,還會給GC帶來壓力,如果頻繁創(chuàng)建線程那么相對的GC的時(shí)候也需要回收對應(yīng)的線程。

      線程池的機(jī)制?

      可以看到線程池是一種重復(fù)利用線程的技術(shù),線程池的主要機(jī)制就是保留一定的線程數(shù)在沒有事情做的時(shí)候使之睡眠,當(dāng)有活干的時(shí)候拿一個(gè)線程去運(yùn)行。
      這些牽扯到線程池實(shí)現(xiàn)的具體策略。

      還有哪些常見的池?

      * 線程池
      * 連接池(數(shù)據(jù)庫連接、TCP連接等)
      * BufferPool
      * ......
      Java中的線程池

      UML圖(Java 8)


      可以看到真正的實(shí)現(xiàn)類有

      * ThreadPoolExecutor (1.5)
      * ForkJoinPool (1.7)
      * ScheduledThreadPoolExecutor (1.5)
      今天我們主要談?wù)?ThreadPoolExecutor 也是使用率較高的一個(gè)實(shí)現(xiàn)。

      Executors提供的工廠方法

      *
      newCachedThreadPool (ThreadPoolExecutor)


      創(chuàng)建一個(gè)可緩存的線程池。如果線程池的大小超過了處理任務(wù)所需要的線程,那么就會回收部分空閑(60秒不執(zhí)行任務(wù))的線程,當(dāng)任務(wù)數(shù)增加時(shí),此線程池又可以智能的添加新線程來處理任務(wù)。此線程池不會對線程池大小做限制,線程池大小完全依賴于操作系統(tǒng)(或者說JVM)能夠創(chuàng)建的最大線程大小。

      *
      newFixedThreadPool (ThreadPoolExecutor)


      創(chuàng)建固定大小的線程池。每次提交一個(gè)任務(wù)就創(chuàng)建一個(gè)線程,直到線程達(dá)到線程池的最大大小。線程池的大小一旦達(dá)到最大值就會保持不變,如果某個(gè)線程因?yàn)閳?zhí)行異常而結(jié)束,那么線程池會補(bǔ)充一個(gè)新線程。

      *
      newSingleThreadExecutor (ThreadPoolExecutor)


      創(chuàng)建一個(gè)單線程的線程池。這個(gè)線程池只有一個(gè)線程在工作,也就是相當(dāng)于單線程串行執(zhí)行所有任務(wù)。如果這個(gè)唯一的線程因?yàn)楫惓=Y(jié)束,那么會有一個(gè)新的線程來替代它。此線程池保證所有任務(wù)的執(zhí)行順序按照任務(wù)的提交順序執(zhí)行。

      *
      newScheduledThreadPool (ScheduledThreadPoolExecutor)

      創(chuàng)建一個(gè)大小無限的線程池。此線程池支持定時(shí)以及周期性執(zhí)行任務(wù)的需求。

      *
      newSingleThreadScheduledExecutor (ScheduledThreadPoolExecutor)

      創(chuàng)建一個(gè)單線程用于定時(shí)以及周期性執(zhí)行任務(wù)的需求。

      *
      newWorkStealingPool (1.8 ForkJoinPool)

      創(chuàng)建一個(gè)工作竊取

      可以看到各種不同的工廠方法中使用的線程池實(shí)現(xiàn)類最終只有3個(gè),對應(yīng)關(guān)系如下:

      工廠方法 實(shí)現(xiàn)類
      newCachedThreadPool ThreadPoolExecutor
      newFixedThreadPool ThreadPoolExecutor
      newSingleThreadExecutor ThreadPoolExecutor
      newScheduledThreadPool ScheduledThreadPoolExecutor
      newSingleThreadScheduledExecutor ScheduledThreadPoolExecutor
      newWorkStealingPool ForkJoinPool
      ThreadPoolExecutor

      首先我們看下 ThreadPoolExecutor 的完全構(gòu)造函數(shù)
      public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long
      keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory
      threadFactory, RejectedExecutionHandler handler)
      *
      corePoolSize

      核心池大小,除非設(shè)置了 allowCoreThreadTimeOut 否則哪怕線程超過空閑時(shí)間,池中也要最少要保留這個(gè)數(shù)目的線程。


      需要注意的是,corePoolSize所需的線程并不是立即創(chuàng)建的,需要在提交任務(wù)之后進(jìn)行創(chuàng)建,所以如果有大量的緩存線程數(shù)可以先提交一個(gè)空任務(wù)讓線程池將線程先創(chuàng)建出來,從而提升后續(xù)的執(zhí)行效率。

      *
      maximumPoolSize

      允許的最大線程數(shù)。

      *
      keepAliveTime

      空閑線程空閑存活時(shí)間,核心線程需要 allowCoreThreadTimeOut 為true才會退出。

      *
      unit

      與 keepAliveTime 配合,設(shè)置 keepAliveTime 的單位,如:毫秒、秒。

      *
      workQueue


      線程池中的任務(wù)隊(duì)列。上面提到線程池的主要作用是復(fù)用線程來處理任務(wù),所以我們需要一個(gè)隊(duì)列來存放需要執(zhí)行的任務(wù),在使用池中的線程來處理這些任務(wù),所以我們需要一個(gè)任務(wù)隊(duì)列。

      *
      threadFactory

      當(dāng)線程池判斷需要新的線程時(shí)通過線程工程創(chuàng)建線程。

      *
      handler


      執(zhí)行被阻止時(shí)的處理程序,線程池?zé)o法處理。這個(gè)與任務(wù)隊(duì)列相關(guān),比如隊(duì)列中可以指定隊(duì)列大小,如果超過了這個(gè)大小該怎么辦呢?JDK已經(jīng)為我們考慮到了,并提供了4個(gè)默認(rèn)實(shí)現(xiàn)。
      下列是JDK中默認(rèn)攜帶的策略:
      *
      AbortPolicy (默認(rèn))

      拋出 RejectedExecutionException 異常。

      *
      CallerRunsPolicy

      調(diào)用當(dāng)前線程池所在的線程去執(zhí)行。

      *
      DiscardPolicy

      直接丟棄當(dāng)前任務(wù)。

      *
      DiscardOldestPolicy

      將最舊的任務(wù)丟棄,將當(dāng)前任務(wù)添加到隊(duì)列。

      容易混淆的參數(shù):corePoolSize maximumPoolSize workQueue

      任務(wù)隊(duì)列、核心線程數(shù)、最大線程數(shù)的邏輯關(guān)系

      * 當(dāng)線程數(shù)小于核心線程數(shù)時(shí),創(chuàng)建線程。
      * 當(dāng)線程數(shù)大于等于核心線程數(shù),且任務(wù)隊(duì)列未滿時(shí),將任務(wù)放入任務(wù)隊(duì)列。
      * 當(dāng)線程數(shù)大于等于核心線程數(shù),且任務(wù)隊(duì)列已滿
      * 若線程數(shù)小于最大線程數(shù),創(chuàng)建線程
      * 若線程數(shù)等于最大線程數(shù),調(diào)用拒絕執(zhí)行處理程序(默認(rèn)效果為:拋出異常,拒絕任務(wù))
      那么這三個(gè)參數(shù)推薦如何設(shè)置,有最優(yōu)值嗎?

      由于java對于協(xié)程的支持不友好,所以會大量依賴于線程池和線程。
      從而這個(gè)值沒有最優(yōu)推薦,需要根據(jù)業(yè)務(wù)需求情況來進(jìn)行設(shè)置。
      不同的需求類型可以創(chuàng)建多個(gè)不同的線程池來執(zhí)行。

      問題1:阿里開發(fā)規(guī)范為什么不允許Executors快速創(chuàng)建線程池?



      參考地址:https://github.com/alibaba/p3c <https://github.com/alibaba/p3c>

      可以看到原因很簡單

      * newSingleThreadExecutor
      * newFixedThreadPool
      在 workQueue 參數(shù)直接 使用了 new LinkedBlockingQueue<Runnable>() 理論上可以無限添加任務(wù)到線程池。
      public static ExecutorService newFixedThreadPool(int nThreads) { return new
      ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new
      LinkedBlockingQueue<Runnable>(); } public static ExecutorService
      newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService(new
      ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new
      LinkedBlockingQueue<Runnable>())); }
      如果提交到線程池的任務(wù)由問題,比如 sleep 永久,會造成內(nèi)存泄漏,最終導(dǎo)致OOM。

      同時(shí) 阿里還推薦自定義 threadFactory 設(shè)置線程名稱便于以后排查問題。

      問題2:下面的代碼輸出是什么?

      應(yīng)該選C。
      雖然最大線程數(shù)有100但核心線程數(shù)為1,任務(wù)隊(duì)列由100。
      滿足了 '當(dāng)線程數(shù)大于等于核心線程數(shù),且任務(wù)隊(duì)列未滿時(shí),將任務(wù)放入任務(wù)隊(duì)列。' 這個(gè)條件。
      所以后續(xù)添加的任務(wù)都會被堵塞。

      最后

      關(guān)于 ThreadPoolExecutor
      的邏輯在實(shí)際使用的時(shí)候會有點(diǎn)奇怪,因?yàn)榫€程池中的線程并沒有超過最大線程數(shù),有沒有一種可能當(dāng)任務(wù)被堵塞很久的時(shí)候創(chuàng)建新的線程池來處理呢?

      這邊推薦大家使用 newWorkStealingPool,也就是ForkJoinPool。采取了工作竊取的模式。
      后續(xù)會跟大家一起聊聊 ForkJoinPool。

      友情鏈接
      ioDraw流程圖
      API參考文檔
      OK工具箱
      云服務(wù)器優(yōu)惠
      阿里云優(yōu)惠券
      騰訊云優(yōu)惠券
      京東云優(yōu)惠券
      站點(diǎn)信息
      問題反饋
      郵箱:[email protected]
      QQ群:637538335
      關(guān)注微信

        <ul id="qxxfc"><fieldset id="qxxfc"><tr id="qxxfc"></tr></fieldset></ul>
          美女啪啪无遮挡免费久久网站 | 少妇激情偷人三级 | 午夜成人免费观看 | 欧美熟妇操逼 | 99久久久无码国产精品免费 | 一级美女黄色片 | 天堂AV秘 永久进入 | 欧美亚洲老女人屄操老男人 | 日韩毛片一区二区 | 強姦婬片A片AAA毛片Mⅴ |