執行緒池的原理及使用
一. 執行緒池的優點
1:降低資源消耗。通過重複利用已建立的執行緒降低執行緒建立和銷燬造成的消耗。
2:提高響應速度。當任務到達時,任務可以不需要等到執行緒建立就能立即執行。
3:提高執行緒的可管理性。執行緒是稀缺資源,如果無限制地建立,不僅會消耗系統資源,
還會降低系統的穩定性,使用執行緒池可以進行統一分配、調優和監控。但是,要做到合理利用
執行緒池,必須對其實現原理了如指掌。
二. 執行緒池的實現
* ThreadPoolExecutor 是實現執行緒池非常核心的一個類
ThreadPoolExecutor ——>繼承了 AbstractExecutorService ——實現了 ExecutorService ——>繼承了 Executor介面
注:Executor是一個頂層介面,在它裡面只聲明瞭一個方法execute(Runnable),返回值為void,引數為Runnable型別,從字面意思可以理解,就是用來執行傳進去的任務的;
public interface Executor {
void execute(Runnable command);
}
* 在ThreadPoolExecutor類中有幾個非常重要的方法:
execute()
submit()
shutdown()
shutdownNow()
1.submit()方法是在ExecutorService中宣告的方法,在AbstractExecutorService就已經有了具體的實現,在ThreadPoolExecutor中並沒有對其進行重寫,這個方法也是用來向執行緒池提交任務的,但是它和execute()方法不同,它能夠返回任務執行的結果,去看submit()方法的實現,會發現它實際上還是呼叫的execute()方法。
2.shutdown():不會立即終止執行緒池,而是要等所有任務快取佇列中的任務都執行完後才終止,但再也不會接受新的任務
3.shutdownNow():立即終止執行緒池,並嘗試打斷正在執行的任務,並且清空任務快取佇列,返回尚未執行的任務
public class ThreadPoolExecutor extends AbstractExecutorService {
.....
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
BlockingQueue<Runnable> workQueue);
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory);
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler);
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler);
...
}
引數解釋:
* corePoolSize:核心執行緒數量,沒有達到次數量執行緒會不斷建立新的執行緒,個人理解就是執行緒池中正常適合容納的執行緒的數量。
maximumPoolSize:執行緒池最多可以容納的執行緒數量,超過這個會觸發飽和策略(RejectedExecutionHandler)
keepAliveTime:表示執行緒沒有任務執行時最多保持多久時間會終止。注意:預設情況下,只有當執行緒池中的執行緒數大於corePoolSize時,keepAliveTime才會起作用,直到執行緒池中的執行緒數不大於corePoolSize,即當執行緒池中的執行緒數大於corePoolSize時,如果一個執行緒空閒的時間達到keepAliveTime,則會終止,直到執行緒池中的執行緒數不超過corePoolSize。
TimeUnit:keepAliveTime的單位。
列舉型別:
TimeUnit.DAYS; //天
TimeUnit.HOURS; //小時
TimeUnit.MINUTES; //分鐘
TimeUnit.SECONDS; //秒
TimeUnit.MILLISECONDS; //毫秒
TimeUnit.MICROSECONDS; //微妙
TimeUnit.NANOSECONDS; //納秒
- BlockingQueue: 阻塞佇列 這裡大致寫一下,詳細請自己查詢
ArrayBlockingQueue; //陣列型別 有界,必須規定佇列大小
LinkedBlockingQueue; //連結串列型別,預設Integer.MAX_VALUE
SynchronousQueue; //一個不儲存元素的阻塞佇列
三. 執行緒的具體使用
程式碼:
public class Task extends Thread{
private int num;
public Task(int num) {
this.num = num;
}
@Override
public void run() {
System.out.println("正在執行任務:" + num);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("結束任務:" + num);
}
}
public class ThreadPool {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(5,
10, 0, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(5));
for(int i=0;i<15;i++){
Task myTask = new Task(i);
executor.execute(myTask);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("執行緒池中執行緒數目:"+executor.getPoolSize()+",佇列中等待執行的任務數目:"+
executor.getQueue().size()+",已執行完別的任務數目:"+executor.getCompletedTaskCount());
}
executor.shutdown();
}
}
通過對任務時間,執行緒的keepAliveTime的調整,觀察執行緒池的狀態。具體如下:
1.線上程的數量沒有達到corePoolSize時,會不斷建立新的執行緒處理任務。
2.達到corePoolSize時會有兩種情況,如果keepAliveTime很小,有執行緒在這個時候是空閒的,就會利用空閒執行緒處理任務,否則就讓任務進入阻塞佇列。
3.阻塞佇列滿時,會接著建立執行緒處理任務,直到到達maximumPoolSize,觸發飽和策略。
四. 飽和策略
RejectedExecutionHandler(飽和策略):
ThreadPoolExecutor.AbortPolicy:丟棄任務並丟擲RejectedExecutionException異常。
ThreadPoolExecutor.DiscardPolicy:也是丟棄任務,但是不丟擲異常。
ThreadPoolExecutor.DiscardOldestPolicy:丟棄佇列最前面的任務,然後重新嘗試執行任務(重複此過程)
ThreadPoolExecutor.CallerRunsPolicy:由呼叫執行緒處理該任務