摘自阿里巴巴開發手冊:
【強制】線程池不允許使用 Executors 去創建,而是通過 ThreadPoolExecutor 的方式,這樣 的處理方式讓寫的同學更加明確線程池的運行規則,規避資源耗盡的風險。 說明:Executors 返回的線程池對象的弊端如下: 1)FixedThreadPool 和 SingleThreadPool: 允許的請求隊列長度爲 Integer.MAX_VALUE,可能會堆積大量的請求,從而導致 OOM。 2)CachedThreadPool 和 ScheduledThreadPool: 允許的創建線程數量爲 Integer.MAX_VALUE,可能會創建大量的線程,從而導致 OOM。
摘自網絡:
1.優勢:
(1)降低資源消耗。通過重複利用已創建的線程降低線程創建、銷燬線程造成的消耗。
(2)提高響應速度。當任務到達時,任務可以不需要等到線程創建就能立即執行。
(3)提高線程的可管理性。線程是稀缺資源,如果入限制的創建,不僅會消耗系統資源,還會降低系統的穩定性,使用線程池可以進行統一的分配、調優和監控。
2.線程池的創建
new ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime, TimeUnit unit,BlockingQueue workQueue,RejectedExecutionHandler handler)
(1)corePoolSize: 線程池維護線程的最少數量 (core : 核心)
(2)maximumPoolSize: 線程池維護線程的最大數量
(3)keepAliveTime: 線程池維護線程所允許的空閒時間
(4)unit: 線程池維護線程所允許的空閒時間的單位
(5)workQueue: 線程池所使用的緩衝隊列
(6)handler: 線程池對拒絕任務的處理策略
3.添加任務到線程池
通過 execute(Runnable)方法被添加到線程池,任務就是一個 Runnable類型的對象,任務的執行方法就是 Runnable類型對象的run()方法。
當一個任務通過execute(Runnable)方法欲添加到線程池時:
如果此時線程池中的數量小於corePoolSize,即使線程池中的線程都處於空閒狀態,也要創建新的線程來處理被添加的任務。
如果此時線程池中的數量等於 corePoolSize,但是緩衝隊列 workQueue未滿,那麼任務被放入緩衝隊列。
如果此時線程池中的數量大於corePoolSize,緩衝隊列workQueue滿,並且線程池中的數量小於maximumPoolSize,建新的線程來處理被添加的任務。
如果此時線程池中的數量大於corePoolSize,緩衝隊列workQueue滿,並且線程池中的數量等於maximumPoolSize,那麼通過 handler所指定的策略來處理此任務。
也就是:處理任務的優先級爲:
核心線程corePoolSize、任務隊列workQueue、最大線程maximumPoolSize,如果三者都滿了,使用handler處理被拒絕的任務。
當線程池中的線程數量大於 corePoolSize時,如果某線程空閒時間超過keepAliveTime,線程將被終止。這樣,線程池可以動態的調整池中的線程數。
unit可選的參數爲java.util.concurrent.TimeUnit中的幾個靜態屬性:NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS。
workQueue常用的是:java.util.concurrent.ArrayBlockingQueue
handler有四個選擇:
ThreadPoolExecutor.AbortPolicy(): 拋出java.util.concurrent.RejectedExecutionException異常
ThreadPoolExecutor.CallerRunsPolicy(): 重試添加當前的任務,他會自動重複調用execute()方法
ThreadPoolExecutor.DiscardOldestPolicy(): 拋棄舊的任務
ThreadPoolExecutor.DiscardPolicy(): 拋棄當前的任務
4.線程池的使用場合
(1)單個任務處理的時間比較短;
(2)需要處理的任務數量大;
6、線程池的應用舉例:
package hh;
import java.io.Serializable;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExcutorDemo {
private static int produceTaskSleepTime = 5;
private static int consumeTaskSleepTime = 5000;
private static int produceTaskMaxNumber = 20; //定義最大添加10個線程到線程池中
public static void main(String[] args) {
//構造一個線程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 4, 3,
TimeUnit. SECONDS, new ArrayBlockingQueue<Runnable>(3),
new ThreadPoolExecutor.DiscardOldestPolicy());
for( int i=1; i<= produceTaskMaxNumber;i++){
try {
//一個任務,並將其加入到線程池
String work= "work@ " + i;
System. out.println( "put :" +work);
threadPool.execute( new ThreadPoolTask(work));
//便於觀察,等待一段時間
Thread. sleep(produceTaskSleepTime);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 線程池執行的任務
* @author zhu
*/
public static class ThreadPoolTask implements Runnable,Serializable{
private static final long serialVersionUID = 0;
//保存任務所需要的數據
private Object threadPoolTaskData;
ThreadPoolTask(Object works){
this. threadPoolTaskData =works;
}
public void run(){
//處理一個任務,這裏的處理方式太簡單了,僅僅是一個打印語句
System. out.println( "start------"+threadPoolTaskData );
try {
//便於觀察,等待一段時間
Thread. sleep(consumeTaskSleepTime);
} catch (Exception e) {
e.printStackTrace();
}
threadPoolTaskData = null;
}
public Object getTask(){
return this. threadPoolTaskData;
}
}
}
執行結果分析:
put :work@ 1 |線程數未達到核心線程數目,新開線程執行work1
start------work@ 1 |work1執行
put :work@ 2 |線程數未達到核心線程數目,新開線程執行work2
start------work@ 2 |work2執行
put :work@ 3 |核心線程數滿,隊列未滿,將work3添加入隊列
put :work@ 4 |核心線程數滿,隊列未滿,將work4添加入隊列
put :work@ 5 |核心線程數滿,隊列未滿,將work5添加入隊列
put :work@ 6 |核心線程數滿,隊列已滿,新開線程執行work6
start------work@ 6 |work6執行
put :work@ 7 |核心線程數滿,隊列已滿,新開線程執行work7
start------work@ 7 |work7執行
put :work@ 8 |最大線程數滿,隊列已滿,按照DiscardOldestHandler丟棄work3,將work8加入隊列
put :work@ 9 |最大線程數滿,隊列已滿,按照DiscardOldestHandler丟棄work4,將work9加入隊列
put :work@ 10 |最大線程數滿,隊列已滿,按照DiscardOldestHandler丟棄work5,將work10加入隊列
put :work@ 11 |
put :work@ 12 |
put :work@ 13 |
put :work@ 14 |
put :work@ 15 |
put :work@ 16 |
put :work@ 17 |
put :work@ 18 |最大線程數滿,隊列已滿,按照DiscardOldestHandler丟棄work15,將work18加入隊列
put :work@ 19 |最大線程數滿,隊列已滿,按照DiscardOldestHandler丟棄work16,將work19加入隊列
put :work@ 20 |最大線程數滿,隊列已滿,按照DiscardOldestHandler丟棄work17,將work20加入隊列
start------work@ 18 |work18開始執行
start------work@ 19 |work19開始執行
start------work@ 20 |work20開始執行