線程池
基本概念: 一種線程使用模式。線程過多會帶來調度開銷,進而影響緩存局部性和整體性能。
不要使用Executor創建線程池,而是通過ThreadPoolExecutor的方式創建,這樣的處理方式能讓編寫代碼的人更加明確線程池的運行規則,規避資源耗盡的風險。
說明:Executors 返回的線程池對象的弊端如下:
1)FixedThreadPool和SingleThreadPool:
允許請求的隊列長度爲Interger.MAX_VALUE,可能會堆積大量的請求,從而導致OOM。
2)CachedThreadPool:
允許的創建線程數量爲:Interger.MAX_VALUE,可能會創建大量的線程,從而導致OOM。
Executor創建線程的3大方法:
//Executor不建議使用,容易引發OOM
ExecutorService threadPool = Executors.newSingleThreadExecutor();//單線程
ExecutorService threadPool1 = Executors.newFixedThreadPool(5);//固定線程
ExecutorService threadPool2 = Executors.newCachedThreadPool();//可變線程
for (int i = 0; i < 10; i++) {
threadPool2.execute(()->{
System.out.println(Thread.currentThread().getName());
});
}
ThreadPoolExecutor的七大參數:
核心線程數,最大線程數,超時等待時間,超時等待單位,阻塞隊列,線程工廠,拒絕策略。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
執行過程:如果線程數沒有超過核心線程數,那麼就會用到幾個開闢幾個,如果大於核心線程數,但是沒有超過阻塞隊列的個數,那麼就會等待覈心線程執行完,再取阻塞隊列中的執行
測試代碼:
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
2,
6,
3,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
try {
for (int i = 0; i < 7; i++) {
final int temp = i;
threadPoolExecutor.execute(()->
{
try {
System.out.println(Thread.currentThread().getName()+" "+temp);
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPoolExecutor.shutdown();
}
調整不同的循環數會出現不同的執行順序,甚至會報錯,具體是什麼原理呢?下面用畫圖的方式描述出來。
執行過程
以核心線程數爲2,最大線程數爲6,阻塞隊列容量爲3 爲例:
- 如果要執行的任務數小於等於核心線程數:
如圖,藍色爲待執行的任務,黃色爲核心線程,當要執行的任務數小於等於核心線程數時,直接被核心線程接管:
- 當待執行的任務數大於核心線程數,但是小於核心線程數+阻塞隊列容量時:
task1,task2被核心線程接管,task3進阻塞隊列等待覈心線程執行完畢再執行。
- 當待執行的任務數大於核心線程數+阻塞隊列容量但小於最大線程數+阻塞隊列容量時:
因爲阻塞隊列已達到最大容量,剩餘的線程(除去核心線程的其他線程)會被啓動,用來直接接管新來的任務,當線程經過keepAliveTime
還沒有接到任務,則再次關閉線程。
注意:直接接管新來的任務,而不是從阻塞隊列中取
- 當待執行的任務數大於了最大線程數+阻塞隊列容量時:
超出數量的任務會被拒絕,具體的拒絕策略有四種:
ThreadPoolExecutor.AbortPolicy()
:該策略是指,當有超出數量的任務來時,會拋出異常。
ThreadPoolExecutor.CallerRunsPolicy();
:超出數量的任務會被調用者線程執行,比如:
因爲是主線程調用的,所以會被主線程接管。
ThreadPoolExecutor.DiscardPolicy();
:當超出數量的任務來時,不會拋出異常。
可以看出當循環次數爲10時,只執行了9個,多出的任務被丟棄。
ThreadPoolExecutor.DiscardOldestPolicy();
:超出數量的任務會嘗試去和最早執行的線程競爭。