線程池ThreadPool

線程池帶來的好處

  • 第一:降低資源消耗姚建。通過重復利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗。
  • 第二:提高響應速度。當任務到達時埠褪,任務可以不需要等到線程創(chuàng)建就能立即執(zhí)行串述。
  • 第三:提高線程的可管理性画畅。線程是稀缺資源谬盐,如果無限制地創(chuàng)建炕置,不僅會消耗系統(tǒng)資源兆沙,還會降低系統(tǒng)的穩(wěn)定性欧芽,使用線程池可以進行統(tǒng)一分配、調優(yōu)和監(jiān)控葛圃。但是千扔,要做到合理利用線程池,必須對其實現原理了如指掌库正。

線程池的實現原理

  • 當向線程池提交一個任務之后曲楚,線程池是如何處理這個任務的呢?

    • 1)線程池判斷核心線程池里的線程是否都在執(zhí)行任務褥符。如果不是龙誊,則創(chuàng)建一個新的工作線程來執(zhí)行任務。如果核心線程池里的線程都在執(zhí)行任務喷楣,則進入下個流程趟大。

    • 2)線程池判斷工作隊列是否已經滿鹤树。如果工作隊列沒有滿,逊朽,則將新提交的任務存儲在 這個工作隊列里罕伯。如果工作隊列滿了,則進入下個流程叽讳。

    • 3)線程池判斷線程池的線程是否都處于工作狀態(tài)追他。如果沒有,則創(chuàng)建一個新的工作線程來執(zhí)行任務岛蚤。如果已經滿了邑狸,則交給飽和策略來處理這個任務

    線程池的主要處理流程
  • ThreadPoolExecutor 執(zhí)行 execute() 方法時的流程如下:

    • 1)如果當前運行的線程少于 corePoolSize,則創(chuàng)建新線程來執(zhí)行任務(注意灭美,執(zhí)行這一步驟需要獲取全局鎖)推溃。

    • 2)如果運行的線程等于或多于 corePoolSize,則將任務加入BlockingQueue(隊列已滿)届腐,則創(chuàng)建新的線程來處理任務(注意,執(zhí)行這一步驟需要獲取全局鎖)蜂奸。

    • 4)如果創(chuàng)建新線程將使當前運行的線程超出 maximumPoolSize犁苏,任務將被拒絕,并調用 RejectedExecutionHandler.rejectedExecution() 方法

      ThreadPoolExecutor執(zhí)行圖

線程池的使用

線程池的創(chuàng)建
  • 我們可以通過 ThreadPoolExecutor 來創(chuàng)建一個線程池:

    • new ThreadPoolExecutor(int corePoolSize,
                                    int maximumPoolSize,
                                    long keepAliveTime,
                                    TimeUnit unit,
                                    BlockingQueue<Runnable> workQueue,
                                    ThreadFactory threadFactory,
                                    RejectedExecutionHandler handler);
      
  • 創(chuàng)建一個線程池時需要輸入幾個參數扩所,如下:

    • 1)corePoolSize(線程池的基本大形辍):當提交一個任務到線程池時,線程池會創(chuàng)建一個線程來執(zhí)行任務祖屏,即使其他空閑的基本線程能夠執(zhí)行新任務也會創(chuàng)建線程助赞,等到需要執(zhí)行的任務數大于線程池基本大小時就不再創(chuàng)建。如果調用了線程池的 prestartAllCoreThreads() 方法袁勺,線程池會提前創(chuàng)建并啟動所有基本線程雹食。

    • 2)runnableTaskQueue(任務隊列):用于保存等待執(zhí)行的任務的阻塞隊列∑诜幔可以選擇以下幾個阻塞隊列

      • ArrayBlockingQueue :是一個基于數組結構的有界阻塞隊列群叶,此隊列按 FIFO(先進先出)原則對元素進行排序。
      • LinkedBlockingQueue :一個基于鏈表結構的阻塞隊列钝荡,此隊列按 FIFO 排序元素街立,吞吐量通常要高于 ArrayBlockingQueue。靜態(tài)工廠方法 Executors.newFixedThreadPool() 使用了這個隊列
      • SynchronousQueue :一個不存儲元素的阻塞隊列埠通。每個插入操作必須等到另一個 線程調用移除操作赎离,否則插入操作一直處于阻塞狀態(tài),吞吐量通常要高于 LinkedBlockingQueue端辱,靜態(tài)工廠方法 Executors.newCachedThreadPool 使用了這個隊列梁剔。
      • PriorityBlockingQueue:一個具有優(yōu)先級的無限阻塞隊列
    • 3)maximumPoolSize(線程池最大數量):線程池允許創(chuàng)建的最大線程數虽画。如果隊列滿了并且已創(chuàng)建的線程數小于最大線程數憾朴,則線程池會再創(chuàng)建新的線程執(zhí)行任務狸捕。值得注意的是,如果使用了無界的任務隊列這個參數就沒什么效果(無界隊列不會滿)众雷。

    • 4)ThreadFactory:用于設置創(chuàng)建線程的工廠灸拍,可以通過線程工廠給每個創(chuàng)建出來的線程設置更有意義的名字。使用開源框架 guava 提供的 ThreadFactoryBuilder 可以快速給線程池里的線程設置有意義的名字砾省,代碼如下:

      • new ThreadFactoryBuilder().setNameFormat("XX-task-%d").build();
    • 5)RejectedExecutionHandler(飽和策略):當隊列和線程池都滿了鸡岗,說明線程池處于飽和狀態(tài),那么必須采取一種策略處理提交的新任務编兄。這個策略默認情況下是 AbortPolicy轩性,表示無法處理新任務時拋出異常。在 JDK 1.5 中 Java 線程池框架提供了以下 4 種策略狠鸳。

      • AbortPolicy:直接拋出異常
      • CallerRunsPolicy:只用調用者所在線程來運行任務揣苏。
      • DiscardOldestPolicy:丟棄隊列里最近的一個任務,并執(zhí)行當前任務件舵。
      • DiscardPolicy:不處理卸察,丟棄掉。
      • 當然铅祸,也可以根據應用場景需要來實現 RejectedExecutionHandler 接口自定義策略坑质。如記錄日志或持久化存儲不能處理的任務。
    • 6) keepAliveTime(線程活動保持時間):線程池的工作線程空閑后临梗,保持存活的時間涡扼。所以,如果任務很多盟庞,并且每個任務執(zhí)行的時間比較短吃沪,可以調大時間,提高線程的利用率茫经。

    • 7) TimeUnit(線程活動保持時間的單位):可選的單位有天(DAYS)巷波、小時(HOURS)、分鐘(MINUTES)卸伞、毫秒(MILLISECONDS)抹镊、微秒(MICROSECONDS,千分之一毫秒)和納秒(NANOSECONDS荤傲,千分之一微秒)垮耳。

向線程池提交任務
  • 可以使用兩個方法向線程池提交任務,分別為 execute() 和 submit() 方法。
    • execute() 方法輸入的任務是一個 Runnable 類的實例终佛。void execute(Runnable command)
    • submit() 方法輸入任務可以是 Runnable 也可以是 Callable 的示例:
      • Future<?> submit(Runnable task);俊嗽,返回的future 調用 get 方法時,返回的結果 null
      • <T> Future<T> submit(Runnable task, T result);铃彰,返回的future 調用 get 方法時绍豁,返回的結果是 result
      • <T> Future<T> submit(Callable<T> task);, 返回的 future 調用get方法時, 是 Callable 實例中 call 方法返回的結果
關閉線程
  • 可以通過調用線程池的 shutdown 或 shutdownNow 方法來關閉線程池牙捉。它們的原理是遍歷線程池中的工作線程竹揍,然后逐個調用線程的 interrupt 方法來中斷線程,所以無法響應中斷的任務可能永遠無法終止邪铲。
  • 但是它們存在一定的區(qū)別芬位,shutdownNow 首先將線程池的狀態(tài)設置成 STOP,然后嘗試停止所有的正在執(zhí)行或暫停任務的線程带到,并返回等待執(zhí)行任務的列表昧碉,
  • shutdown 只是將線程池的狀態(tài)設置SHUTDOWN 狀態(tài),然后中斷所有沒有正在執(zhí)行任務的線程

合理地配置線程池

  • 分析的角度:
    • 任務的性質:CPU 密集型任務揽惹、IO 密集型任務和混合型任務被饿。
    • 任務的優(yōu)先級:高、中和低搪搏。
  • 性質不同的任務可以用不同規(guī)模的線程池分開處理
    • CPU 密集型任務應配置盡可能小的線程锹漱,如配置 Ncpu+1 個線程的線程池。
    • 由于 IO 密集型任務線程并不是一直在執(zhí)行任務慕嚷,則應配置盡可能多的線程,如 2*Ncpu毕泌。
    • 混合型的任務喝检,如果可以拆分,將其拆分成一個 CPU 密集型任務和一個 IO 密集型任務撼泛,只要這兩個任務執(zhí)行的時間相差不是太大挠说,那么分解后執(zhí)行的吞吐量將高于串行執(zhí)行的吞吐量。如果這兩個任務執(zhí)行時間相差太大愿题,則沒必要進行分解损俭。
    • 可以通過 Runtime.getRuntime().availableProcessors() 方法獲得當前設備的 CPU 個數。
    • 優(yōu)先級不同的任務可以使用優(yōu)先級隊列 PriorityBlockingQueue 來處理潘酗。它可以讓優(yōu)先級
      高的任務先執(zhí)行杆兵。
    • 執(zhí)行時間不同的任務可以交給不同規(guī)模的線程池來處理,或者可以使用優(yōu)先級隊列仔夺,讓執(zhí)行時間短的任務先執(zhí)行琐脏。
    • 依賴數據庫連接池的任務,因為線程提交 SQL 后需要等待數據庫返回結果,等待的時間越長日裙,則 CPU 空閑時間就越長吹艇,那么線程數應該設置得越大,這樣才能更好地利用 CPU昂拂。
  • 建議使用有界隊列受神。有界隊列能增加系統(tǒng)的穩(wěn)定性和預警能力,可以根據需要設大一點兒格侯,比如幾千鼻听。

參考 《Java并發(fā)編程藝術》、《Java并發(fā)編程實戰(zhàn)》

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末养交,一起剝皮案震驚了整個濱河市精算,隨后出現的幾起案子,更是在濱河造成了極大的恐慌碎连,老刑警劉巖灰羽,帶你破解...
    沈念sama閱讀 221,430評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異鱼辙,居然都是意外死亡廉嚼,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 94,406評論 3 398
  • 文/潘曉璐 我一進店門倒戏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來怠噪,“玉大人,你說我怎么就攤上這事杜跷“睿” “怎么了?”我有些...
    開封第一講書人閱讀 167,834評論 0 360
  • 文/不壞的土叔 我叫張陵葛闷,是天一觀的道長憋槐。 經常有香客問我,道長淑趾,這世上最難降的妖魔是什么阳仔? 我笑而不...
    開封第一講書人閱讀 59,543評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮扣泊,結果婚禮上近范,老公的妹妹穿的比我還像新娘。我一直安慰自己延蟹,他們只是感情好评矩,可當我...
    茶點故事閱讀 68,547評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著等孵,像睡著了一般稚照。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,196評論 1 308
  • 那天果录,我揣著相機與錄音上枕,去河邊找鬼。 笑死弱恒,一個胖子當著我的面吹牛辨萍,可吹牛的內容都是我干的。 我是一名探鬼主播返弹,決...
    沈念sama閱讀 40,776評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼锈玉,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了义起?” 一聲冷哼從身側響起拉背,我...
    開封第一講書人閱讀 39,671評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎默终,沒想到半個月后椅棺,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 46,221評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡齐蔽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,303評論 3 340
  • 正文 我和宋清朗相戀三年两疚,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片含滴。...
    茶點故事閱讀 40,444評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡诱渤,死狀恐怖,靈堂內的尸體忽然破棺而出谈况,到底是詐尸還是另有隱情勺美,我是刑警寧澤,帶...
    沈念sama閱讀 36,134評論 5 350
  • 正文 年R本政府宣布碑韵,位于F島的核電站励烦,受9級特大地震影響,放射性物質發(fā)生泄漏泼诱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,810評論 3 333
  • 文/蒙蒙 一赊锚、第九天 我趴在偏房一處隱蔽的房頂上張望治筒。 院中可真熱鬧,春花似錦舷蒲、人聲如沸耸袜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,285評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽堤框。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蜈抓,已是汗流浹背启绰。 一陣腳步聲響...
    開封第一講書人閱讀 33,399評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留沟使,地道東北人委可。 一個月前我還...
    沈念sama閱讀 48,837評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像腊嗡,于是被迫代替她去往敵國和親着倾。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,455評論 2 359

推薦閱讀更多精彩內容