單線程池
- newSingleThreadExecutor建立
池中保持一個線程,最多也隻有一個線程,也就是說這個線程池是順序執行任務的,多餘的任務就在隊列中排隊。
固定線程池
- newFixedThreadPool(nThreads)建立
池中保持nThreads個線程,最多也隻有nThreads個線程,多餘的任務也在隊列中排隊。
線程數固定且線程不逾時。
緩存線程池
- newCachedThreadPool()建立
池中不保持固定數量的線程,而是按需建立,最多可建立Integer.MAX_VALUE個線程,這已大大超過目前任何os允許的線程數。
空閑的線程最多保持60s,多餘的任務在SynchronousQueue等待。
适用場景
- 耗時較短的任務
- 任務處理速度 > 任務送出速度 ,這樣才能保證不會不斷建立新的程序,避免記憶體被占滿。
線程池中的線程是被線程池緩存了的,即:
線程沒有任務執行時,便處于空閑狀态,處于空閑狀态的線程并不會被立即銷毀(會被緩存),隻有當空閑時間超出一段時間(預設60s)後,線程池才會銷毀該線程(相當于清除過時緩存)
新任務到達後,線程池首先會讓被緩存住的線程(空閑狀态)去執行任務,若無可用線程(無空閑線程),便會建立新的線程。
為什麼使用SynchronousQueue()?
因為單線程池和固定線程池中,線程數量有限,是以送出的任務需要在LinkedBlockingQueue隊列中等待空閑線程。
而緩存線程池中,線程數量幾乎無限(上限為Integer.MAX_VALUE),是以送出的任務隻需要在SynchronousQueue隊列中同步移交給空餘線程即可。
固定排程線程池
- newScheduledThreadPool(n)建立
池中保持n個線程,多餘的任務在DelayedWorkQueue中等待。
有一項技術可以緩解執行時間較長任務造成的影響,即限定任務等待資源的時間,而不要無限的等待.
先看第一個例子,測試單線程池、固定線程池和緩存線程池(注意增加和取消注釋):
public class ThreadPoolExam {
public static void main(String[] args) {
//first test for singleThreadPool
ExecutorService pool = Executors.newSingleThreadExecutor();
//second test for fixedThreadPool
// ExecutorService pool = Executors.newFixedThreadPool(2);
//third test for cachedThreadPool
// ExecutorService pool = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
pool.execute(new TaskInPool(i));
}
pool.shutdown();
}
}
class TaskInPool implements Runnable {
private final int id;
TaskInPool(int id) {
this.id = id;
}
@Override
public void run() {
try {
for (int i = 0; i < 5; i++) {
System.out.println("TaskInPool-["+id+"] is running phase-"+i);
TimeUnit.SECONDS.sleep(1);
}
System.out.println("TaskInPool-["+id+"] is over");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
如圖為排查底層公共緩存調用出錯時的截圖
- 有意義的線程命名
綠色框采用自定義的線程工廠,明顯比藍色框預設的線程工廠建立的線程名稱擁有更多的額外資訊:如調用來源、線程的業務含義,有助于快速定位到死鎖、StackOverflowError 等問題。
Executors類提供的一些快捷聲明線程池的方法雖然簡單,但隐藏了線程池的參數細節。是以,使用線程池時,我們一定要根據場景和需求配置合理的線程數、任務隊列、拒絕政策、線程回收政策,并對線程進行明确的命名友善排查問題。