[TOC]
聲明
該系列文章只是記錄本人回顧java多線程編程時候記錄的筆記。文中所用語言并非嚴謹的專業術語(太嚴謹的術語其實本人也不會……)。難免有理解偏差的地方,歡迎指正。
另外,大神請繞路。不喜勿噴。
畢竟好記性不如爛筆頭嘛,而且許多東西只要不是你經常用的最終都會一丟丟一丟丟地給忘記。
1 線程池簡介
1.2 什么是線程池?
通俗點講,線程池就是一個裝有一大堆線程的容器。
這就和我們通常使用的JDBC的各種數據源有點類似了。數據源里的元素是改造過可重復利用
的數據庫連接(Connection)的實例。而線程池里的元素就是可重復利用
的線程了。
1.2 使用線程池有什么好處?
- 降低開銷: 主要是線程的創建和銷毀的開銷(提前創建)
- 提高響應速度: 省去了線程的創建和銷毀的階段,能立即響應業務處理
- 更好的管理線程: 線程池是線程的容器, 線程放在容器里更方便統一監控和管理
以下是《java并發編程的藝術》一書中對線程池工作流程的流程圖
線程池的處理流程
2 ThreadPoolExecutor
2.1 相關屬性
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, 10, 60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100)
);
- corePoolSize: 線程池基本大小
- 將一個待執行的任務放到線程池中的時候,如果待執行的任務數小于該參數,線程池就會創建一個線程來執行該任務。反之,不再創建新的線程。
- 在線程池剛剛創建后,線程并不是立即啟動的。如果事先調用了
prestartCoreThread()
或者prestartAllCoreThreads()
方法則會立即啟動核心線程。
- poolSize: 當前大小(實際線程數)
- maximumPoolSize: 線程池中所能允許的最大線程數
- 如果隊列滿了,并且poolSize < maximumPoolSize,在新任務到來后會創建新的線程來執行。
- 如果底層使用的是無界隊列,該參數將無意義
- allowCoreThreadTimeOut: 是否允許核心線程超時退出
- 在沒有任務執行的時候,是否允許核心線程超時退出
-
false
: 表示即使是空閑時,核心核心線程也不會退出
- keepAliveTime: 是否允許線程超時退出
- 此處指的是超出核心線程數的那部分線程(corePoolSize < poolSize < maximumPoolSize)
- 而核心線程是否超時退出由
allowCoreThreadTimeOut
控制
- workQueue | runnableTaskQueue : 用來保存待執行任務的阻塞隊列
- ArrayBlockingQueue
- LinkedBlockingQueue
- SynchronousQueue
- PriorityBlockingQueue
- 對于這幾個隊列不熟悉的可以參考 : http://blog.csdn.net/hylexus/article/details/53451307
- threadFactory: 創建線程的工廠
- RejectedExecutionHandler: 飽和處理策略,見下文
2.2 飽和處理策略
如果使用的是有界隊列,很可能出現隊列和線程池飽和的情況,RejectedExecutionHandler
就是用來指定如何處理這種情況的。
內置的四種實現如下:
- AbortPolicy: 拋出異常
- CallerRunsPolicy: 在調用
execute()
方法的線程里執行飽和的任務 - DiscardOldestPolicy: discards the oldest unhandled request
- 此處不知道怎么翻譯了,
oldest
可以理解為最靠近隊首
的元素 - 或者說是
最近
的
- 此處不知道怎么翻譯了,
- DiscardPolicy: 直接丟棄
2.3 提交/執行任務
線程池創建好之后,下一步就是向線程池提交任務讓線程池中的線程去執行。
提交的任務必須是Callable
或者Runnable
的實現類。
提交任務,有如下方法:
public void execute(Runnable command){}
public Future<?> submit(Runnable task){}
public <T> Future<T> submit(Callable<T> task){}
public <T> Future<T> submit(Runnable task, T result){}
其中,execute()
方法用于提交沒有返回值的任務。所以無法得知提交的任務什么時候被執行,也無法知道何時執行完畢了。
this.executor.execute(() -> {
// task to executed
});
submit()方法用于提交帶有返回值的任務。
Future<String> future = this.executor.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return null;
}
});
try {
String result = future.get();
System.out.println(result);
} catch (InterruptedException e) {
// 中斷
e.printStackTrace();
} catch (ExecutionException e) {
// 任務執行錯誤
e.printStackTrace();
} finally {
this.executor.shutdown();
}
2.4 停止線程池
提供了兩個方法來終止線程池: shutdown()
和shutdownNow()
shutdown
shutdownNow
這兩個方法都是通過遍歷線程,逐個調用線程的interrupt()方法來終止線程的。
interrupt
所以問題來了:
無法響應中斷的線程,可能就沒法終止了
另外,在shutdown()
方法中,還調用了onShutdown
方法。這個方法方法體的實現是空的。
可以重寫這個hook方法來在線程池銷毀的時候完成一些其他的事情。
參考資料
- 《java并發編程的藝術》