線程池
簡介
線程池的創建
new ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit milliseconds,
BlockingQueue<Runnable> runnableTaskQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler);
- corePoolSize(線程池基本大小)
這是一個判斷該線程池是否要新建一個新的線程的標準值,如果當前線程池中的線程數小於corePoolSize,
則在接到新的請求任務時新建一個線程來處理,反之則把任務放到
BlockingQueue中,由線程池中空的線程從BlockingQueue中取出並處理;
- maximumPoolSize(線程池最大大小)
線程池最大的線程數,當大於該值的時候則讓RejectedExecutionHandler拒絕處理;
keepAliveTime
當線程池中大於corePoolSize的時候,部分多餘的空線程會等待keepAliveTime時間,如果沒有請求處理超過該時間則自行銷燬;BlockingQueue(任務隊列)
保存等待任務執行的阻塞隊列,有以下幾種:- ArrayBlockingQueue:數組隊列,先進先出;
- LinkedBlockingQueue:鏈表隊列,先進先出,吞吐量大於ArrayBlockingQueue;
- SynchronousQueue:一個不存儲元素的隊列,每插入一個任務必須等到另一個線程調用移除操作,否則處於阻塞狀態;吞吐量高於LinkedBlockingQueue,newCachedThreadPool使用的就是這個隊列;
- PriorityBlockingQueue:一個優先級無限阻塞的隊列;
RejectedExecutionHandler(飽和策略)
當線程池處於飽和狀態下,對於提交的新任務必須要有一種策略來處理;
提交任務
execute(new Runnable(){} )
沒有返回值threadsPool.execute(new Runnable() { @Override public void run() { // TODO Auto-generated method stub } });
commit(new callable(){}) 返回future對象
Future future = mThreadPoolExecutor.submit(new Callable() { @Override public Object call() throws Exception { return null; } }); -------------------------------------------------- try { Object o = future.get();//阻塞 直到結果準備就緒 } catch (InterruptedException e) { //中斷異常 e.printStackTrace(); } catch (ExecutionException e) { //無法執行異常 e.printStackTrace(); } finally { //關閉線程池 mThreadPoolExecutor.shutdown(); } }
關閉任務
- 原理
遍歷所有的線程,逐個調用線程的interrupt方法來中斷線程; - shutdown
執行shutdown後,遍歷線程池中所有的線程,將狀態修改爲SHUTDOWN狀態,然後中斷正在執行任務的線程; - shutdownNow
執行shutdownNow後,遍歷線程池中所有的線程,將所有線程的狀態修改爲STOP狀態,然後嘗試停止所有的線程任務。
工作原理
- 工作流程示意圖
源碼分析
工作線程
合理配置線程池
任務特性
- 任務的性質:CPU密集型任務,IO密集型的任務,混合型任務;
- 任務的優先級:高,中,低;
- 任務的執行時間:長、中、短;
- 任務的依賴性,是否依賴其他系統資源,比如數據庫連接。
配置意見
- CPU密集型建議使用線程數儘可能少的線程池,IO密集型任務由於線程並不是一直在工作,所以建議使用線程數較多的線程池;
- 優先級不同任務可以使用PriorityBlockingProcessors任務隊列來處理,讓優先級更高的來處理(!注意:如果一直是優先級搞的任務在處理,則優先級低的任務可能無法得到處理;
- 時間不同的任務可以交給不同規模的線程池來處理,也可以交給優先級隊列,讓時間短的任務先執行;
- 依賴數據庫連接池的任務,由於提交SQL後需要等待返回結果,所以等待的時間越長,CPU空閒的時間越長,爲了提高CPU的利用率,可以通過增大線程池的線程數。
- 建議使用有界隊列,這樣更加穩定安全,防止撐爆內存(如果線程一直處於阻塞狀態,新的任務來的時候就會新建新的線程,直到內存不足)。
線程池的監控
部分線程池的屬性參數
- taskCount:線程池需要執行的任務數量;
- completedTaskCont: 已經完成的任務數量;
- largestPoolSize: 線程池曾經創建過的最大線程數,可以看看是否大於最大線程數,是否滿過;
- getPoolSize: 線程池的線程數量,線程池不銷燬,池中的線程不會自動銷燬;
- getAliveCount:活動狀態的線程數。
通過擴展線程池進行監控
- 繼承線程池;
- 重寫beforeExecute,afterExecute和terminated方法。
常用線程池
FixedThreadPool 定長併發線程池
源碼
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
- 特點:
可以創建固定的線程數,當線程數達到corePoolSize的時候,再添加任務就放到LinkedBlockingQueue這個無界的隊列裏邊,等待空閒的線程來取任務執行任務,空閒線程不會被回收;
SingleThreadExecutor 順序執行線程池
源碼
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
- 特點
corePoolSize爲1,既最多只能有一個線程工作,當其他任務來的時候都放到LinkedBlockingQueue任務隊列裏邊,等待線程工作完後,再從隊列裏邊取,逐個執行,相當於順序執行;
CachedThreadPool “無限”容量可緩存線程池
源碼
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
- 特點
- SynchronousQueue是一個沒有容量的阻塞隊列,每個插入必須有對應的移除操作;
- 如果沒有線程去從SynchronousQueue從事移除的工作,那麼就會新建一個線程來執行任務,如果一直這麼下去,就可以能因爲創建過多的線程耗盡CPU資源;
- 如果空閒的線程等待時間超過60秒,而且沒有新的任務,會自動被回收。
- ScheduledThreadPool 定時線程池
參考資料:
- JAVA編程思想
- JDK1.6源碼
- 聊聊併發——方騰飛
- Java中常見的線程池