java線程池原理淺析

問題與解決:

問題:

查詢大數據量的時候,例如一次返回50w數據量的包,循環去查詢發現讀取會超時。

解決方案:

經過思考採用多線程去分頁查詢。使用線程池創建多個線程去查詢分頁後的數據最後彙總一下,解決了一次查詢大量數據返回超時的問題。

一次查詢現狀:



 

 

多線程分頁查詢改造圖:



 

 

效果:使用多線程去查詢查詢時間由原來的10s減少到現在的300ms。

線程池是什麼?

線程池(Thread Pool)是一種基於池化思想管理線程的工具,經常出現在多線程服務器中,如MySQL,clickhouse等數據庫。

線程池好處:

1.降低資源消耗:通過池化技術重複利用已創建的線程,降低線程創建和銷燬造成的損耗。

2.提高響應速度:任務到達時,無需等待線程創建即可立即執行。

3.提高線程的可管理性:線程是稀缺資源,如果無限制創建,不僅會消耗系統資源,還會因爲線程的不合理分佈導致資源調度失衡,降低系統的穩定性。

4.使用線程池可以進行統一的分配、調優和監控。提供更多更強大的功能:線程池具備可拓展性,允許開發人員向其中增加更多的功能。比如延時定時線程池ScheduledThreadPoolExecutor,就允許任務延期執行或定期執行。

線程池解決的問題是什麼:

1.頻繁申請/銷燬資源和調度資源,將帶來額外的消耗,可能會非常巨大。

2.對資源無限申請缺少抑制手段,易引發系統資源耗盡的風險。

3.系統無法合理管理內部的資源分佈,會降低系統的穩定性。

Java線程池參數解析:

Java 提供的線程池相關的工具類中,最核心的是 ThreadPoolExecutor,完備的構造函數如下:

ThreadPoolExecutor(
 int corePoolSize,
 int maximumPoolSize,
 long keepAliveTime,
 TimeUnit unit,
 BlockingQueue<Runnable> workQueue,
 ThreadFactory threadFactory,
 RejectedExecutionHandler handler) 

參數說明:

corePoolSize:線程池的核心線程數

備註:默認情況下,線程池創建後的初始線程數爲 0,當有任務到來就會創建一個線程去執行任務,當線程池中的線程數目達到 corePoolSize,就會把到達的任務放到緩存隊列當中。

maximumPoolSize:線程池的最大線程數

備註:核心線程忙不過來且任務存儲隊列滿了的情況下,還有新任務進來的話就會繼續開闢線程,但是也不是任意的開闢線程數量,線程數(包含核心線程)達到maximumPoolSize後就不會產生新線程了,就會執行拒絕策略。

keepAliveTime:當活躍線程數大於核心線程數時,空閒的多餘線程最大存活時間

備註:默認情況下,當線程池中的線程數大於 corePoolSize 時,如果一個線程空閒的時間達到keepAliveTime,則該線程會終止,直到線程池中的線程數不超過 corePoolSize。但是如果調用了 allowCoreThreadTimeOut(true) 方法,此時就算線程池中的線程數不大於corePoolSize ,keepAliveTime 參數也會起作用,直到線程池中的線程數爲 0。

unit:參數 keepAliveTime 的時間單位。

取值:

TimeUnit.DAYS;               //天
TimeUnit.HOURS;             //小時 
TimeUnit.MINUTES;           //分鐘 
TimeUnit.SECONDS;           //秒
TimeUnit.MILLISECONDS;      //毫秒 
TimeUnit.MICROSECONDS;      //微妙
TimeUnit.NANOSECONDS;       //納秒 

workQueue:任務阻塞隊列,用來存儲等待執行的任務。

備註:

核心線程數滿了後還有任務繼續提交到線程池的話,就先進入workQueue。

workQueue通常情況下有如下選擇:

LinkedBlockingQueue:無界隊列,意味着無限制,其實是有限制,大小是int的最大值。也可以自定義大小。

ArrayBlockingQueue:有界隊列,可以自定義大小,到了閾值就開啓新線程(不會超過maximumPoolSize)。

SynchronousQueue:Executors.newCachedThreadPool();默認使用的隊列。也不算是個隊列,他不沒有存儲元素的能力。

一般都採取LinkedBlockingQueue,因爲他也可以設置大小,可以取代ArrayBlockingQueue有界隊列。

threadFactory:用於創建新線程的線程工廠。

備註:默認採用的是DefaultThreadFactory,主要負責創建線程。newThread()方法。創建出來的線程都在同一個線程組且優先級也是一樣的。

handler:拒絕策略,任務量超出線程池的配置限制或執行shutdown還在繼續提交任務的話,會執行handler的邏輯。

備註:

如果線程池中所有的線程都在忙碌,並且工作隊列也滿了(前提是工作隊列是有界隊列),那麼此時提交任務,線程池就會拒絕接收。拒絕策略可以通過 handler 參數來指定。

ThreadPoolExecutor 提供了以下4種拒絕策略:

CallerRunsPolicy:提交任務的線程自己去執行該任務。

AbortPolicy:默認的拒絕策略,會 throws RejectedExecutionException。

DiscardPolicy:直接丟棄任務,沒有任何異常拋出。

DiscardOldestPolicy:丟棄最老的任務,就是把最早進入工作隊列的任務丟棄,然後把新任務加入到工作隊列。

線程池工作原理:



 

 



java提供的常用線程池:

1.newCachedThreadPool:用來創建一個可以無限擴大的線程池,適用於負載較輕的場景,執行短期異步任務。(可以使任務快速得到執行,因爲任務時間執行短,可以很快結束,也不會造成cpu過度切換)

2.newFixedThreadPool:創建一個固定大小的線程池,因爲採用無界的阻塞隊列,所以實際線程數量永遠不會變化,適用於負載較重的場景,對當前線程數量進行限制。(保證線程數可控,不會造成線程過多,導致系統負載更爲嚴重)

3.newSingleThreadExecutor:創建一個單線程的線程池,適用於需要保證順序執行各個任務。

4.newScheduledThreadPool:適用於執行延時或者週期性任務。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章