執行緒池的建立和使用,執行緒池原始碼初探(篇一)

private_static發表於2020-10-24


今天來總結一下執行緒池的使用和幾個重要引數吧,本來自己業務並沒有用到執行緒池。但是執行緒池的使用場景還是挺廣泛的。特別是高併發、多執行緒的情況下。最近也嘗試了一下jump cao,也是個常遇到個考點吧。就當自我總結,做個筆記了。


1、執行緒池的介紹

  • 執行緒池是一種執行緒使用模式。執行緒過多會帶來排程開銷,進而影響快取區域性性和整體效能。而執行緒池維護著多個執行緒,等待著監督管理者分配可併發執行的任務。這避免了在處理短時間任務時建立與銷燬執行緒的代價。執行緒池不僅能夠保證核心的充分利用,還能防止過分排程。可用執行緒數量應該取決於可用的併發處理器、處理器核心、記憶體、網路sockets等的數量。

2、執行緒池的優點:

  • a、執行緒池可以降低系統資源消耗,通過一次建立多個執行緒放線上程池中,當任務來臨時。重用已存在的執行緒,可以降低執行緒建立和銷燬造成的消耗。
  • b、提高系統響應速度,當有任務到達時,直接可呼叫已建立的執行緒,節省執行緒建立造成的時間損耗。
  • c、可以方便執行緒併發數的管控。當任務過多,執行緒無限制的建立,可能會導致記憶體佔用過多而產生OOM,而且還會造成cpu過度切換,而cpu切換執行緒是有時間成本的(需要保持當前執行執行緒的現場,並恢復要執行執行緒的現場)。
  • d、還可以設定執行緒池定時啟動等功能。

3、主要引數介紹

  • java.uitl.concurrent.ThreadPoolExecutor類是執行緒池中最核心的一個類,因此如果要透徹地瞭解Java中的執行緒池,必須先了解這個類。可以先看一下ThreadPoolExecutor 類原始碼裡的這四個方法:
    在這裡插入圖片描述
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:最大執行緒數、keepAliveTime:執行緒存活時間、unit:時間單位、workQueue:阻塞佇列…

a、corePoolSize:核心執行緒數
  • 這個參數列示的是執行緒建立的時候預設建立的執行緒數,會預建立這麼多條執行緒先放在裡面。即使當前沒有任務、也會建立這麼多執行緒。
b、maximumPoolSize:最大執行緒數
  • 執行緒池最大執行緒數。如果執行緒數量少於執行緒最大數且大於核心執行緒數量的時候,只有當阻塞佇列滿了才建立新執行緒。當執行緒數量大於最大執行緒數且阻塞佇列滿了這時候就會執行一些策略來響應該執行緒。
c、keepAliveTime:表示執行緒沒有任務執行時最多保持多久時間會終止。
  • 預設情況下,只有當執行緒池中的執行緒數大於corePoolSize時,keepAliveTime才會起作用,即當執行緒池中的執行緒數大於corePoolSize時,如果一個執行緒空閒的時間達到keepAliveTime,則會終止,直到執行緒池中的執行緒數不超過corePoolSize。但是如果呼叫了allowCoreThreadTimeOut(boolean)方法,線上程池中的執行緒數不大於corePoolSize時,keepAliveTime引數也會起作用,直到執行緒池中的執行緒數為0。
d、unit:引數keepAliveTime的時間單位
  • unit有7種取值,在TimeUnit類中有7種靜態屬性:
	TimeUnit.DAYS;               //天
	TimeUnit.HOURS;             //小時
	TimeUnit.MINUTES;           //分鐘
	TimeUnit.SECONDS;           //秒
	TimeUnit.MILLISECONDS;      //毫秒
	TimeUnit.MICROSECONDS;      //微妙
	TimeUnit.NANOSECONDS;       //納秒
e、workQueue:阻塞佇列
  • 用來儲存等待執行的任務,這個引數的選擇也很重要,會對執行緒池的執行過程產生重大影響,一般來說,這裡的阻塞佇列有以下幾種選擇:
   	ArrayBlockingQueue;
   	LinkedBlockingQueue;
   	SynchronousQueue;

說都說到這兒了,繼續把剩下兩個介紹了吧。

f、threadFactory:執行緒工廠,主要用來建立執行緒。
g、handler: 表示當拒絕處理任務時的策略,有以下四種取值:

	ThreadPoolExecutor.AbortPolicy:丟棄任務並丟擲RejectedExecutionException異常。
	 
	ThreadPoolExecutor.DiscardPolicy:也是丟棄任務,但是不丟擲異常。 
	
	ThreadPoolExecutor.DiscardOldestPolicy:丟棄佇列最前面的任務,然後重新嘗試執行任務(重複此過程)

	ThreadPoolExecutor.CallerRunsPolicy:由呼叫執行緒處理該任務  
	

4、執行緒池的五種狀態

  • RUNNING: 在這個狀態的執行緒池能判斷接受新提交的任務,並且也能處理阻塞佇列中的任務
  • SHUTDOWN: 處於關閉的狀態,該執行緒池不能接受新提交的任務,但是可以處理阻塞佇列中已經儲存的任務,線上程處於RUNNING狀態,呼叫shutdown()方法能切換為該狀態。
  • STOP: 執行緒池處於該狀態時既不能接受新的任務也不能處理阻塞佇列中的任務,並且能中斷現線上程中的任務。當執行緒處於RUNNING和SHUTDOWN狀態,呼叫shutdownNow()方法就可以使執行緒變為該狀態
  • TIDYING: 在SHUTDOWN狀態下阻塞佇列為空,且執行緒中的工作執行緒數量為0就會進入該狀態,當在STOP狀態下時,只要執行緒中的工作執行緒數量為0就會進入該狀態。
  • TERMINATED: 在TIDYING狀態下呼叫terminated()方法就會進入該狀態。可以認為該狀態是最終的終止狀態。

5、執行緒池常用方法

  • submit():提交任務,能夠返回執行結果execute+Future

  • shutdown():關閉執行緒池,等待任務都執行完

  • shutdownNow():關閉執行緒池,不等待任務執行完

  • getTaskCount():執行緒池已執行和未執行的任務總數

  • getCompletedTaskCount():已完成的任務數量

  • getPoolSize():執行緒池當前執行緒數量

  • getActiveCount():當前執行緒池中正在執行任務的執行緒數量


注: 由於博主業務切實沒涉及到,之前也就只在安卓開發時常用到多執行緒。安卓那個東西執行緒一言不合就閃退。由於沒有高併發需求,所以執行緒池這塊也是自己的盲區,簡單總結下概念問題。後續有時間在研究吧。如有理解錯誤的地方,還望路過的大佬指正!


我攤牌了,我就是來嫖1024勳章的,哈哈…但是也有認真在總結。好!外賣到了,就今天先寫到這兒!祝大家節日快樂!哈哈哈!

相關文章