Java線程池
線程池的作用
1.重用存在的線程,減少處理多請求時線程創建、銷毀產生的開銷。
2.請求達到時工作線程通常已經存在,請求無需等待,提高系統的響應性。
Executors中線程池的靜態工廠方法
1.newFixedThreadPool
創建一個定長的線程池,每當提交一個任務就創建一個線程,直到達到池的最大長度,這時線程池會保持長度不再變化。
2.newCachedThreadPool
創建一個可緩存的線程池,如果當前線程池的長度超過了處理的需要時,它可以靈活地回收空閑的線程,當需求增加時,它可以靈活添加新的線程。
3.newSingleThreadExecutor
創建一個單線程化的executor,它只創建唯一的工作者線程來執行任務,executor會保證任務依照任務隊列所規定的順序(FIFO、LIFO、優先級)執行。
4.newScheduledThreadPool
創建一個定長的線程池,而且支持定時的以及周期性的任務執行,類似於Timer。
ThreadPoolExecutor類中線程池的構造函數
/** * 通過給定的初始化參數創建一個線程池(ThreadPoolExecutor) * * @param corePoolSize 線程池的核心線程數,核心線程即使空閑也不會被銷毀,
* 除非allowCoreThreadTimeOut被設置 * @param maximumPoolSize 線程池中允許的最大線程數 * @param keepAliveTime 表示當前線程數大於核心線程數時,空閑線程被銷毀前
* 等待新任務到達的最大時間 * @param unit keepAliveTime參數的時間單位 * @param workQueue 在任務被執行前用來存放的隊列,這個隊列將僅僅存放execute方法提交的Runnable任務 * @param threadFactory executor創建新線程所用的工廠 * @param handler 由於線程數量和隊列容量已滿,執行被阻塞時的拒絕策略 * @throws IllegalArgumentException if one of the following holds:<br> * {@code corePoolSize < 0}<br> * {@code keepAliveTime < 0}<br> * {@code maximumPoolSize <= 0}<br> * {@code maximumPoolSize < corePoolSize} * @throws NullPointerException if {@code workQueue} * or {@code threadFactory} or {@code handler} is null */ 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.acc = System.getSecurityManager() == null ? null : AccessController.getContext(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
線程池的參數
1.核心線程數:即使在空閑時也不會被銷毀的線程數
2.最大線程數:線程池中允許的最大線程數
3.保持存活時間:空閑線程在被銷毀之前等待新任務的時間
4.存活時間單位:等待時間的單位
5.工作隊列:存放待執行任務的隊列
6.線程工廠:新線程創建的工廠
7.拒絕策略:當線程數量和隊列已滿時采取的拒絕策略
工作隊列
工作隊列有多種,但都實現BlockQueue接口
隊列類型 | 線程池類型 | 特點 |
LinkedBlockingQueue | FixedThreadPool SingleThreadPool 使用的工作隊列 |
基於鏈表的先進先出隊列 |
SynchronousQueue | CachedThreadPool使用的工作隊列 | 不保存提交的任務,而是直接創建一個線程來執行新任務 |
DelayedWorkQueue | ScheduledThreadPool使用的工作隊列 |
拒絕策略
1.AbortPolicy:直接丟棄任務並拋出RejectedExecutionException異常。
2.CallerRunsPolicy:只要線程池未關閉,該策略直接在調用者線程中,運行當前被丟棄的任務。
3.DiscardOldestPolicy:丟棄隊列中最老的一個任務,也就是即將要執行的任務,並再次嘗試提交當前任務。
4.DiscardPolicy:丟棄任務,不做任何處理。
線程池任務處理策略
線程池的任務處理如下圖所示。
邏輯較為清晰,其實這是線程池execute()方法的邏輯,下面看一下源碼:
//通過execute向線程池提交任務
public void execute(Runnable command) { if (command == null) throw new NullPointerException(); int c = ctl.get();
//如果當前線程數未達到核心線程數,則直接創建線程來執行新任務 if (workerCountOf(c) < corePoolSize) { if (addWorker(command, true)) return; c = ctl.get(); }
//否則將任務加入阻塞隊列,這里進行雙重檢查,如果線程池已經關閉,則調用reject(),
//如果當前線程池線程數為0,則新建線程 if (isRunning(c) && workQueue.offer(command)) { int recheck = ctl.get(); if (! isRunning(recheck) && remove(command)) reject(command); else if (workerCountOf(recheck) == 0) addWorker(null, false); }
//如果加入阻塞隊列失敗,則嘗試新建一個線程,如果失敗了
//則說明線程池關閉了或者線程達到最大線程數,因此調用reject() else if (!addWorker(command, false)) reject(command); }
線程池提供了兩個方法,用來關閉線程池。
(1)shutdown():不會立即關閉線程池,但也不接受新的任務,等待隊列中所有任務執行完畢后關閉。
(2)shutdownNow():立即終止線程池,並嘗試打斷正在執行的任務,清空工作隊列,返回尚未執行的任務。
線程池的線程數應該如何設置
Nthreads = Ncpu * (1 + w/c) w為阻塞時間,c為計算時間
IO密集型:w/c>1,因此線程數應該為cpu的數倍,但需要考慮線程所占內存,因此通常將線程數設置為cpu的2倍。
CPU密集型:w/c=0,因此線程數為CPU個數。通常將線程數設置為CPU+1,防止線程由於偶爾出現的原因而暫停。