JAVA 線程池的正確打開方式

JAVA 線程池的正確打開方式

摘要:本文屬於原創,歡迎轉載,轉載請保留出處: https://github.com/jasonGeng88/blog

當前環境

  1. jdk == 1.8

Executors 使用的隱患

先來看一段代碼,咱們要建立一個固定線程池,假設固定線程數是4。代碼以下:git

Executors是JAVA併發包中提供的,用來快速建立不一樣類型的線程池。github

是否是很簡單,建立線程池只需一行代碼。對於一些我的項目或臨時性的項目,這樣寫確實沒什麼問題,並且開發速度很快。但在一些大型項目中,這種作法通常是禁止的。併發

WHY???異步

由於用Executors建立的線程池存在性能隱患,咱們看一下源碼就知道,用Executors建立線程池時,使用的隊列是new LinkedBlockingQueue<Runnable>(),這是一個無邊界隊列,若是不斷的往裏加任務時,最終會致使內存問題,也就是說在項目中因爲使用了無邊界隊列,致使的內存佔用的不可控性。下圖是不斷添加線程任務致使老年代被佔滿的狀況:性能

固然,除了內存問題,它還存在一些其餘的問題,在下面對線程池參數的介紹中會具體說明。spa

線程池的正確建立方式

其實,問題很好解決。提供的簡便方式有侷限性,那咱們本身new一個ThreadPoolExecutor,無非多寫幾行代碼而已。線程

關於ThreadPoolExecutor的具體代碼以下:code

參數說明:

  • corePoolSize:核心線程數;
  • maximumPoolSize:最大線程數,即線程池中容許存在的最大線程數;
  • keepAliveTime:線程存活時間,對於超過核心線程數的線程,當線程處理空閒狀態下,且維持時間達到keepAliveTime時,線程將被銷燬;
  • unit:keepAliveTime的時間單位
  • workQueue:工做隊列,用於存在待執行的線程任務;
  • threadFactory:建立線程的工廠,用於標記區分不一樣線程池所建立出來的線程;
  • handler:當到達線程數上限或工做隊列已滿時的拒絕處理邏輯;

具體代碼

  • 自定義threadFactory。除了能夠自定義建立的線程名稱,方便問題排查,在newThread(Runnable r)建立線程的方法中,還能夠進行定製化設置,如爲線程設置特定上下文等。

  • 自定義RejectedExecutionHandler。記錄異常信息,選擇不一樣處理邏輯,有交由當前線程執行任務,有直接拋出異常,再或者等待後繼續添加任務等。

  • 建立自定義線程池

線程池內在處理邏輯

咱們經過一些例子,來觀察一下其內部的處理邏輯。基於上述具體代碼,咱們已經建立了一個核心線程數4,最大線程數8,線程存活時間10s,工做隊列最大容量爲10的一個線程池。blog

  • 初始化線程池:未添加線程任務隊列

    • 這時,線程池中不會建立任何線程,存活線程爲0,工做隊列爲0.
  • 未達核心線程數:添加4個線程任務

    • 因爲當前存活線程數 <= 核心線程數,因此會建立新的線程。即存活線程爲4,工做隊列爲0.
  • 核心線程數已滿:添加第5個線程任務

    • 若當前線程池中存在空閒線程,則交由該線程處理。即存活線程爲4,工做隊列爲0.
    • 若當前全部線程處理運行狀態,加入工做隊列。即存活線程爲4,工做隊列爲1.(注意:此時工做隊列中的任務不會被執行,直到有線程空閒後,才能被處理
  • 工做隊列未滿:假設添加的任務都是耗時操做(短期不會結束),再添加9個耗時任務

    • 即存活線程爲4,工做隊列爲10.
  • 工做隊列已滿 & 未達最大線程數:再添加4個任務

    • 當工做隊列已滿,且不存在空閒線程,此時會建立額外線程來處理當前任務。此時存活線程爲8,工做隊列爲10.
  • 工做隊列已滿 & 且最大線程數已滿:再添加1個任務

    • 觸發RejectedExecutionHandler,將當前任務交由本身設置的執行句柄進行處理。此時存活線程爲8,工做隊列爲10.
  • 當任務執行完後,沒有新增的任務,臨時擴充的線程(大於核心線程數的)將在10s(keepAliveTime)後被銷燬。

總結

最後,咱們在使用線程池的時候,須要根據使用場景來自行選擇。經過corePoolSize和maximumPoolSize的搭配,存活時間的選擇,以及改變隊列的實現方式,如:選擇延遲隊列,來實現定時任務的功能。併發包Executors中提供的一些方法確實好用,但咱們仍需有保留地去使用,這樣在項目中就不會挖太多的坑。

擴展

對於一些耗時的IO任務,盲目選擇線程池每每不是最佳方案。經過異步+單線程輪詢,上層再配合上一個固定的線程池,效果可能更好。相似與Reactor模型中selector輪詢處理

相關文章
相關標籤/搜索