示例
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; public class Ch09_Executor { private static void run(ExecutorService threadPool) { for(int i = 1; i < 5; i++) { final int taskID = i; threadPool.execute(new Runnable() { @Override public void run() { for(int i = 1; i < 5; i++) { try { Thread.sleep(20);// 為了測試出效果,讓每次任務執行都需要一定時間 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("第" + taskID + "次任務的第" + i + "次執行"); } } }); } threadPool.shutdown();// 任務執行完畢,關閉線程池 } public static void main(String[] args) { // 創建可以容納3個線程的線程池 ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3); // 線程池的大小會根據執行的任務數動態分配 ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); // 創建單個線程的線程池,如果當前線程在執行任務時突然中斷,則會創建一個新的線程替代它繼續執行任務 ExecutorService singleThreadPool = Executors.newSingleThreadExecutor(); // 效果類似於Timer定時器 ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3); run(fixedThreadPool); // run(cachedThreadPool); // run(singleThreadPool); // run(scheduledThreadPool); } }
CachedThreadPool
CachedThreadPool會創建一個緩存區,將初始化的線程緩存起來。會終止並且從緩存中移除已有60秒未被使用的線程。
如果線程有可用的,就使用之前創建好的線程,
如果線程沒有可用的,就新創建線程。
- 重用:緩存型池子,先查看池中有沒有以前建立的線程,如果有,就reuse;如果沒有,就建一個新的線程加入池中
- 使用場景:緩存型池子通常用於執行一些生存期很短的異步型任務,因此在一些面向連接的daemon型SERVER中用得不多。
- 超時:能reuse的線程,必須是timeout IDLE內的池中線程,缺省timeout是60s,超過這個IDLE時長,線程實例將被終止及移出池。
- 結束:注意,放入CachedThreadPool的線程不必擔心其結束,超過TIMEOUT不活動,其會自動被終止。
// 線程池的大小會根據執行的任務數動態分配 ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, //core pool size Integer.MAX_VALUE, //maximum pool size 60L, //keep alive time TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
執行結果:
第1次任務的第1次執行
第4次任務的第1次執行
第3次任務的第1次執行
第2次任務的第1次執行
第3次任務的第2次執行
第4次任務的第2次執行
第2次任務的第2次執行
第1次任務的第2次執行
第2次任務的第3次執行
第4次任務的第3次執行
第3次任務的第3次執行
第1次任務的第3次執行
第2次任務的第4次執行
第1次任務的第4次執行
第3次任務的第4次執行
第4次任務的第4次執行
4個任務是交替執行的。
FixedThreadPool
在FixedThreadPool中,有一個固定大小的池。
如果當前需要執行的任務超過池大小,那么多出的任務處於等待狀態,直到有空閑下來的線程執行任務,
如果當前需要執行的任務小於池大小,空閑的線程也不會去銷毀。
- 重用:fixedThreadPool與cacheThreadPool差不多,也是能reuse就用,但不能隨時建新的線程
- 固定數目:其獨特之處在於,任意時間點,最多只能有固定數目的活動線程存在,此時如果有新的線程要建立,只能放在另外的隊列中等待,直到當前的線程中某個線程終止直接被移出池子
- 超時:和cacheThreadPool不同,FixedThreadPool沒有IDLE機制(可能也有,但既然文檔沒提,肯定非常長,類似依賴上層的TCP或UDP IDLE機制之類的),
- 使用場景:所以FixedThreadPool多數針對一些很穩定很固定的正規並發線程,多用於服務器
- 源碼分析:從方法的源代碼看,cache池和fixed 池調用的是同一個底層池,只不過參數不同:
fixed池線程數固定,並且是0秒IDLE(無IDLE)
cache池線程數支持0-Integer.MAX_VALUE(顯然完全沒考慮主機的資源承受能力),60秒IDLE
// 創建可以容納3個線程的線程池 ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3); public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, //core pool size nThreads, //maximum pool size 0L, //keep alive time TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
執行結果:
第1次任務的第1次執行
第3次任務的第1次執行
第2次任務的第1次執行
第3次任務的第2次執行
第2次任務的第2次執行
第1次任務的第2次執行
第3次任務的第3次執行
第1次任務的第3次執行
第2次任務的第3次執行
第3次任務的第4次執行
第1次任務的第4次執行
第2次任務的第4次執行
第4次任務的第1次執行
第4次任務的第2次執行
第4次任務的第3次執行
第4次任務的第4次執行
創建了一個固定大小的線程池,容量為3,然后循環執行了4個任務。由輸出結果可以看到,前3個任務首先執行完,然后空閑下來的線程去執行第4個任務。
SingleThreadExecutor
SingleThreadExecutor得到的是一個單個的線程,這個線程會保證你的任務執行完成。
如果當前線程意外終止,會創建一個新線程繼續執行任務,這和我們直接創建線程不同,也和newFixedThreadPool(1)不同。
// 創建單個線程的線程池,如果當前線程在執行任務時突然中斷,則會創建一個新的線程替代它繼續執行任務 ExecutorService singleThreadPool = Executors.newSingleThreadExecutor(); public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, //core pool size 1, //maximum pool size 0L, //keep alive time TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
執行結果:
第1次任務的第1次執行
第1次任務的第2次執行
第1次任務的第3次執行
第1次任務的第4次執行
第2次任務的第1次執行
第2次任務的第2次執行
第2次任務的第3次執行
第2次任務的第4次執行
第3次任務的第1次執行
第3次任務的第2次執行
第3次任務的第3次執行
第3次任務的第4次執行
第4次任務的第1次執行
第4次任務的第2次執行
第4次任務的第3次執行
第4次任務的第4次執行
4個任務是順序執行的。
ScheduledThreadPool
ScheduledThreadPool是一個固定大小的線程池,與FixedThreadPool類似,執行的任務是定時執行。
// 效果類似於Timer定時器 ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3); public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, //core pool size Integer.MAX_VALUE, //maximum pool size 0, //keep alive time TimeUnit.NANOSECONDS, new DelayedWorkQueue()); }
執行結果:
第1次任務的第1次執行
第2次任務的第1次執行
第3次任務的第1次執行
第2次任務的第2次執行
第1次任務的第2次執行
第3次任務的第2次執行
第2次任務的第3次執行
第1次任務的第3次執行
第3次任務的第3次執行
第2次任務的第4次執行
第1次任務的第4次執行
第3次任務的第4次執行
第4次任務的第1次執行
第4次任務的第2次執行
第4次任務的第3次執行
第4次任務的第4次執行
——與FixedThreadPool的區別?