1.線程池及線程池的作用:
線程池使應用能夠更加充分合理地協調利用CPU、內存、網絡、I/O等系統資源。
線程的創建需要開辟虛擬機棧、本地方法棧、程序技術器等線程私有的空間。
在線程銷毀時需要回收這些系統資源。頻繁地創建和銷毀線程會浪費大量的系統資源,增加並發編程風險。
另外,在服務器負載過大的時候,如何讓新的線程等待或者友好的拒絕服務?這些都是線程自身無法解決的。
所以需要通過線程池協調多個線程,並實現類似主次線程隔離、定時執行、周期執行等任務。
線程池的作用:
(1)利用線程池管理並復用線程、控制最大並發數等。
(2)實現任務線程隊列緩存策略和拒絕機制。
(3)實現某些與時間相關的功能,如定時執行、周期執行等。
(4)隔離線程環境。比如,交易服務和搜索服務在同一台服務器上,分別開啟兩個線程池,交易線程的資源消耗明顯要大;
因此,通過配置獨立的線程池,將較慢的交易服務與較快的搜索服務隔離開,避免各自服務線程相互影響。
2.線程池是如何創建線程的:
首先從ThreadPoolExecutor構造方法學起,學習如何自定義ThreadFactory和RejectedExecutionHandler,並編寫一個間的的線程池示例。然后通過
通過分析ThreadPoolExecutor的execute和addWorker兩個核心方法,學習如何把任務線程加入到線程池中運行。ThreadPoolExecutor的構造方法如下:
public ThreadPoolExecutor( int corePoolSize, //第1個參數
int maximumPoolSize, //第2個參數
long keepAliveTime, //第3個參數
TimeUnit unit, //第4個參數
BlockingQueue<Runnable> workQueue, //第5個參數
ThreadFactory threadFactory, //第6個參數
RejectedExecutionHandler handler) { //第7個參數
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; }
各個參數的含義:
- 第1個參數 :corePoolSize 表示常駐核心線程數。如果等於0,則任務執行完成后,沒有任何請求進入時銷毀線程池的線程;如果大於0,即使本地任務執行完畢,核心線程也不會被銷毀。這個值的設置非常關鍵,設置過大會浪費資源,設置的過小會導致線程頻繁地創建或銷毀。
- 第2個參數:maximumPoolSize 表示線程池能夠容納同時執行的最大線程數。從上方的示例代碼中第一處來看,必須大於或等於1。如果待執行的線程數大於此值,需要借助第5個參數的幫助。緩存在隊列中。如果maximumPoolSize 與corePoolSize 相等,即是固定大小線程池。
- 第3個參數:keepAliveTime 表示線程池中的線程空閑時間,當空閑時間達到KeepAliveTime 值時,線程被銷毀,直到剩下corePoolSize 個線程為止,避免浪費內存和句柄資源。在默認情況下,當線程池的線程大於corePoolSize 時,keepAliveTime 才會起作用。但是ThreadPoolExecutor的allowCoreThreadTimeOut 變量設置為ture時,核心線程超時后也會被回收。
- 第4個參數:TimeUnit 表示時間單位。keepAliveTime 的時間單位通常是TimeUnit.SECONDS。
- 第5個參數: workQueue 表示緩存隊列。如果線程池里的線程數大於corePoolSize ,就會放到緩存隊列,緩存隊列滿了會創建新線程到maximumPoolsize;直到當請求的線程數大於maximumPoolSize時,會執行設定的策略,默認是拒絕創建策略。(注意:當線程池里的線程數大於corePoolSize且小於maximumPoolSize時,這時候再有請求的線程就會放到緩存隊列,注意只是放到緩存隊列但是不創建新的線程,直到請求的線程存滿緩存隊列時,才會開始創建新的線程,直到maxmunPoolSize就會拒絕創建或者執行提前設定的策略。
- 第6個參數:threadFactory 表示線程工廠。它用來生產一組相同任務的線程。線程池的命名是通過給這個factory增加組名前綴來實現的。在虛擬機棧分析時,就可以知道線程任務是由哪個線程工廠產生的。
- 第7個參數:handler 表示執行拒絕策略的對象。當超過第5個參數workQueue的任務緩存區上限的時候,就可以通過該策略處理請求,這是一種簡單的限流保護。友好的拒絕策略可以使如下三種:
- 保存到數據庫進行削峰填谷。在空閑的時候再拿出來執行。
- 轉向某個提示頁面。
- 打印日志。