天天看點

最詳細的Java線程池原了解析

一、為什麼要用線程池

  1. 降低資源消耗。通過重複利用已建立的線程降低線程建立、銷毀線程造成的消耗。
  2. 提高響應速度。當任務到達時,任務可以不需要等到線程建立就能立即執行。
  3. 提高線程的可管理性。線程是稀缺資源,如果無限制的建立,不僅會消耗系統資源,還會降低系統的穩定性,使用線程池可以進行統一的配置設定、調優和監控

二、ThreadPoolExecutor線程池類參數詳解

參數 說明
corePoolSize 核心線程數量,線程池維護線程的最少數量
maximumPoolSize 線程池維護線程的最大數量
keepAliveTime 線程池除核心線程外的其他線程的最長空閑時間,超過該時間的空閑線程會被銷毀
unit keepAliveTime的機關,TimeUnit中的幾個靜态屬性:NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS
workQueue 線程池所使用的任務緩沖隊列
threadFactory 線程工廠,用于建立線程,一般用預設的即可
handler 線程池對拒絕任務的處理政策

當線程池任務處理不過來的時候(什麼時候認為處理不過來後面描述),可以通過handler指定的政策進行處理,ThreadPoolExecutor提供了四種政策:

  1. ThreadPoolExecutor.AbortPolicy:丢棄任務并抛出RejectedExecutionException異常;也是預設的處理方式。
  2. ThreadPoolExecutor.DiscardPolicy:丢棄任務,但是不抛出異常。
  3. ThreadPoolExecutor.DiscardOldestPolicy:丢棄隊列最前面的任務,然後重新嘗試執行任務(重複此過程)
  4. ThreadPoolExecutor.CallerRunsPolicy:由調用線程處理該任務

可以通過實作RejectedExecutionHandler接口自定義處理方式。

三、線程池任務執行

1. 添加執行任務

  • submit() 該方法傳回一個Future對象,可執行帶傳回值的線程;或者執行想随時可以取消的線程。Future對象的get()方法擷取傳回值。Future對象的cancel(true/false)取消任務,未開始或已完成傳回false,參數表示是否中斷執行中的線程
  • execute() 沒有傳回值。

2. 線程池任務送出過程

2.1. 如果此時線程池中的數量小于corePoolSize,即使線程池中的線程都處于空閑狀态,也要建立新的線程來處理被添加的任務。

2.2. 如果此時線程池中的數量等于corePoolSize,但是緩沖隊列workQueue未滿,那麼任務被放入緩沖隊列。

2.3. 如果此時線程池中的數量大于等于corePoolSize,緩沖隊列workQueue滿,并且線程池中的數量小于maximumPoolSize,建新的線程來處理被添加的任務。

2.4. 如果此時線程池中的數量大于corePoolSize,緩沖隊列workQueue滿,并且線程池中的數量等于maximumPoolSize,那麼通過 handler所指定的政策來處理此任務。

2.5. 當線程池中的線程數量大于 corePoolSize時,如果某線程空閑時間超過keepAliveTime,線程将被終止。這樣,線程池可以動态的調整池中的線程數。

總結即:處理任務判斷的優先級為 核心線程corePoolSize、任務隊列workQueue、最大線程maximumPoolSize,如果三者都滿了,使用handler處理被拒絕的任務。

注意:

  1. 當workQueue使用的是×××限隊列時,maximumPoolSize參數就變的無意義了,比如new LinkedBlockingQueue(),或者new ArrayBlockingQueue(Integer.MAX_VALUE);
  2. 使用SynchronousQueue隊列時由于該隊列沒有容量的特性,是以不會對任務進行排隊,如果線程池中沒有空閑線程,會立即建立一個新線程來接收這個任務。maximumPoolSize要設定大一點。
  3. 核心線程和最大線程數量相等時keepAliveTime無作用.

3. 線程池關閉

3.1. shutdown() 不接收新任務,會處理已添加任務

3.2. shutdownNow() 不接受新任務,不處理已添加任務,中斷正在處理的任務

4. 常用隊列介紹

4.1. ArrayBlockingQueue: 這是一個由數組實作的容量固定的有界阻塞隊列.

4.2. SynchronousQueue: 沒有容量,不能緩存資料;每個put必須等待一個take; offer()的時候如果沒有另一個線程在poll()或者take()的話傳回false。

4.3. LinkedBlockingQueue: 這是一個由單連結清單實作的預設×××的阻塞隊列。LinkedBlockingQueue提供了一個可選有界的構造函數,而在未指明容量時,容量預設為Integer.MAX_VALUE。

隊列操作:

方法
add 增加一個元索; 如果隊列已滿,則抛出一個異常
remove 移除并傳回隊列頭部的元素; 如果隊列為空,則抛出一個異常
offer 添加一個元素并傳回true; 如果隊列已滿,則傳回false
poll 移除并傳回隊列頭部的元素; 如果隊列為空,則傳回null
put 添加一個元素; 如果隊列滿,則阻塞
take 移除并傳回隊列頭部的元素; 如果隊列為空,則阻塞
element 傳回隊列頭部的元素; 如果隊列為空,則抛出一個異常
peek 傳回隊列頭部的元素; 如果隊列為空,則傳回null

5. Executors線程工廠類

1. Executors.newCachedThreadPool();

說明: 建立一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則建立線程.

内部實作:new ThreadPoolExecutor(0,Integer.MAX_VALUE,60L,TimeUnit.SECONDS,new SynchronousQueue<runnable>());</runnable>

2. Executors.newFixedThreadPool(int);

說明: 建立一個定長線程池,可控制線程最大并發數,超出的線程會在隊列中等待。

内部實作:new ThreadPoolExecutor(nThreads, nThreads,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<runnable>());</runnable>

3. Executors.newSingleThreadExecutor();

說明:建立一個單線程化的線程池,它隻會用唯一的工作線程來執行任務,保證所有任務按照順序執行。

内部實作:new ThreadPoolExecutor(1,1,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<runnable>())</runnable>

4. Executors.newScheduledThreadPool(int);

說明:建立一個定長線程池,支援定時及周期性任務執行。

内部實作:new ScheduledThreadPoolExecutor(corePoolSize)

【附】阿裡巴巴Java開發手冊中對線程池的使用規範

  1. 【強制】建立線程或線程池時請指定有意義的線程名稱,友善出錯時回溯。

    正例:

    public class TimerTaskThread extends Thread {
    public TimerTaskThread(){
        super.setName("TimerTaskThread"); 
        ...
    }
    }           
  2. 【強制】線程池不允許使用 Executors 去建立,而是通過 ThreadPoolExecutor 的方式,這樣

    的處理方式讓寫的同學更加明确線程池的運作規則,規避資源耗盡的風險。

6. 總結