目錄
- 線程池優點
- 線程池七大參數介紹
- Executors提供的api創建線程池
- 手動創建線程池,new ThreadPoolExecutor
- 線程池底層工作原理
正文
-
線程池優點
線程是稀缺資源,使用線程池可以減少創建和銷燬線程的次數,減小系統的開銷
每個工作線程都可以重複使用,提高資源使用率
可以根據系統的承受能力,調整線程池中工作線程的數量,防止因爲消耗過多內存導致服務器崩潰 -
線程池的七大參數介紹
看源代碼都可以知道,創建線程池最終都是要使用到下面的那個構造方法public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
一共七個參數
/** * 參數解釋: * corePoolSize:核心線程數,包括空閒線程數 * maximumPoolSize:最大線程數,允許的最多線程數(當請求大於核心線程數以及等待隊列的最大等待數時,就繼續創建線程,但不能超過這裏規定的最大線程) * keepAliveTime:當 當前線程數 > 核心線程數 ,這些多出來的線程在沒有被使用(任務調用)的情況下的存活最長時間 * unit:時間單位 * BlockingQueue<Runnable>:等待隊列(當請求超過核心線程數的時候,多餘的請求進入這裏的等待隊列) * ThreadFactory:線程創建工廠,生成線程的方式,一般默認 DefaultThreadFactory * RejectedExecutionHandler:拒絕策略,當請求越來越多,超過了最大線程數以及等待隊列的最大存放數,那這個時候就需要拒絕接下來的請求了,提供的拒絕策略 */
Java提供了四種拒絕策略
a:AbortPolicy:直接拋出異常
b:CallerRunsPolicy:返回給調用者所在的線程運行該任務
c:DiscardOldestPolicy:丟棄隊列裏最近的一個任務,並執行當前任務
d:DiscardPolicy:不處理,丟棄掉
-
Executors提供的api創建線程池
Executors跟Executor的關係就像Collections和Collection的關係
Executor是線程池的頂級父接口,其下有很多實現子類
Executors是爲了操作線程池提供的一些封裝好的Api常見創建線程池方法:
newFixedThreadPool// 定長線程池,線程池中線程數固定,核心線程數等於最大線程數 ExecutorService threadPool = Executors.newFixedThreadPool(6);
// 源代碼,對照着參數可以很簡單的理解 public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
newSingleThreadExecutor
// 單線程線程池,池中就只有一個線程,最大線程數也是1 ExecutorService threadPool = Executors.newSingleThreadExecutor();
// 源碼 public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
newCachedThreadPool
// 可緩衝線程池,核心線程數爲0,但可以動態擴展到越來越大,同時也有失活時間 ExecutorService threadPool = Executors.newCachedThreadPool();
// 源碼 public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
-
手動創建線程池,new ThreadPoolExecutor
首先看一下阿里巴巴Java開發手冊,來分析一下爲什麼阿里官方不推薦我們使用Executors提供的api創建線程池
理解起來還是很簡單的,上面也看了相關源碼以及七大參數的意思1)使用newFixedThreadPool和newSingleThreadExecutor申明的等待隊列
new LinkedBlockingQueue()無參構造方法,那默認最大容量就是Integer.MAX_VALUE,就是一個無界隊列
這樣一來當有大量請求來的時候,他們都會在堆積在等待隊列中(不會生效拒絕策略),從而引發內存溢出等異常
2)使用newCachedThreadPool和ScheduledThreadPool定義的最大線程數都是Integer.MAX_VALUE
同樣的道理,當有大量請求來的時候,會不斷地創建線程數(不會生效拒絕策略),從而引發內存溢出等異常
3)推薦我們使用new ThreadPoolExecutor方式自己手動創建線程池,這樣可以根據自己服務器的配置合理的設置線程池參數,不至於像上面那樣僅適用於學習理論教學階段的使用,然後實際開發生產過程中將會遇到一系列問題
// 示例 ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 10, 30L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(5), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
-
線程池工作原理
簡單的畫了一下,工作原理(工作流程)還有一點需要注意的是,當 當前線程數 > 核心線程數 ,這些多出來的線程在沒有被使用(任務調用)的情況下的有一個存活最長時間,利用這種機制可以及時回收那些多創建的線程(大於線程數)
-
最後總結
這只是我學習線程池的入門第一步,大致瞭解了什麼是線程池、線程池的參數意義、創建方式、工作原理等一些注意事項
真正還需要深入的瞭解,那就要多看看底層的源代碼了
一起加油哈