這篇文章將為大家詳細講解有關Java線程池知識點有哪些,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
阿里巴巴開發手冊中關于線程和線程池的使用有如下三條強制規約
【強制】創建線程或線程池時請指定有意義的線程名稱,方便出錯時回溯。
正例:自定義線程工廠,并且根據外部特征進行分組,比如,來自同一機房的調用,把機房編號賦值給whatFeatureOfGroup
public class UserThreadFactory implements ThreadFactory {
private final String namePrefix;
private final AtomicInteger nextId = new AtomicInteger(1);
/**
* 定義線程組名稱,在利用 jstack 來排查問題時,非常有幫助
*/
UserThreadFactory(String whatFeatureOfGroup) {
namePrefix = "From UserThreadFactory's " + whatFeatureOfGroup + "-Worker-";
}
@Override
public Thread newThread(Runnable task) {
String name = namePrefix + nextId.getAndIncrement();
Thread thread = new Thread(null, task, name, 0);
System.out.println(thread.getName());
return thread;
}
}【強制】線程資源必須通過線程池提供,不允許在應用中自行顯式創建線程。
說明:線程池的好處是減少在創建和銷毀線程上所消耗的時間以及系統資源的開銷,解決資源不足的問題。
如果不使用線程池,有可能造成系統創建大量同類線程而導致消耗完內存或者“過度切換”的問題。
【強制】線程池不允許使用 Executors 去創建,而是通過 ThreadPoolExecutor 的方式,這
樣的處理方式讓寫的同學更加明確線程池的運行規則,規避資源耗盡的風險。
說明:Executors 返回的線程池對象的弊端如下:
1) FixedThreadPool 和 SingleThreadPool:
允許的請求隊列長度為 Integer.MAX_VALUE,可能會堆積大量的請求,從而導致 OOM。
2) CachedThreadPool:
允許的創建線程數量為 Integer.MAX_VALUE,可能會創建大量的線程,從而導致 OOM。
UML圖:

ThreadPoolExecutor的構造函數共有四個,但最終調用的都是同一個:

corePoolSize => 線程池核心線程數量
maximumPoolSize => 線程池最大數量
keepAliveTime => 線程池的工作線程空閑后,保持存活的時間。如果任務多而且任務的執行時間比較短,可以調大keepAliveTime,提高線程的利用率。
unit => 時間單位
workQueue => 線程池所使用的緩沖隊列,隊列類型有:
ArrayBlockingQueue,基于數組結構的有界阻塞隊列,按FIFO(先進先出)原則對任務進行排序。使用該隊列,線程池中能創建的最大線程數為maximumPoolSize
LinkedBlockingQueue,基于鏈表結構的無界阻塞隊列,按FIFO(先進先出)原則對任務進行排序,吞吐量高于ArrayBlockingQueue。使用該隊列,線程池中能創建的最大線程數為corePoolSize。靜態工廠方法 Executor.newFixedThreadPool()使用了這個隊列。
SynchronousQueue,一個不存儲元素的阻塞隊列。添加任務的操作必須等到另一個線程的移除操作,否則添加操作一直處于阻塞狀態。靜態工廠方法 Executor.newCachedThreadPool()使用了這個隊列。
PriorityBlokingQueue:一個支持優先級的無界阻塞隊列。使用該隊列,線程池中能創建的最大線程數為corePoolSize。
threadFactory => 線程池創建線程使用的工廠
handler => 線程池對拒絕任務的處理策略,主要有4種類型的拒絕策略:
AbortPolicy:無法處理新任務時,直接拋出異常,這是默認策略。
CallerRunsPolicy:用調用者所在的線程來執行任務。
DiscardOldestPolicy:丟棄阻塞隊列中最靠前的一個任務,并執行當前任務。
DiscardPolicy:直接丟棄任務。

如果當前運行的線程少于corePoolSize,則創建新的工作線程來執行任務(執行這一步驟需要獲取全局鎖)。
如果當前運行的線程大于或等于corePoolSize,而且BlockingQueue未滿,則將任務加入到BlockingQueue中。
如果BlockingQueue已滿,而且當前運行的線程小于maximumPoolSize,則創建新的工作線程來執行任務(執行這一步驟需要獲取全局鎖)。
如果當前運行的線程大于或等于maximumPoolSize,任務將被拒絕,并調用RejectExecutionHandler.rejectExecution()方法。即調用飽和策略對任務進行處理。


執行邏輯說明:
判斷核心線程數是否已滿,核心線程數大小和corePoolSize參數有關,未滿則創建線程執行任務
若核心線程池已滿,判斷隊列是否滿,隊列是否滿和workQueue參數有關,若未滿則加入隊列中
若隊列已滿,判斷線程池是否已滿,線程池是否已滿和maximumPoolSize參數有關,若未滿創建線程執行任務
若線程池已滿,則采用拒絕策略處理無法執執行的任務,拒絕策略和handler參數有關
Executors創建返回ThreadPoolExecutor對象的方法共有三種:
corePoolSize => 0,核心線程池的數量為0
maximumPoolSize => Integer.MAX_VALUE,可以認為最大線程數是無限的
keepAliveTime => 60L
unit => 秒
workQueue => SynchronousQueue
弊端:maximumPoolSize => Integer.MAX_VALUE可能會導致OOM
SingleThreadExecutor是單線程線程池,只有一個核心線程:
corePoolSize => 1,核心線程池的數量為1
maximumPoolSize => 1,只可以創建一個非核心線程
keepAliveTime => 0L
unit => 毫秒
workQueue => LinkedBlockingQueue
弊端:LinkedBlockingQueue是長度為Integer.MAX_VALUE的隊列,可以認為是無界隊列,因此往隊列中可以插入無限多的任務,在資源有限的時候容易引起OOM異常
corePoolSize => 1,核心線程池的數量為1
maximumPoolSize => 1,只可以創建一個非核心線程
keepAliveTime => 0L
unit => 毫秒
workQueue => LinkedBlockingQueue
它和SingleThreadExecutor類似,唯一的區別就是核心線程數不同,并且由于使用的是LinkedBlockingQueue,在資源有限的時候容易引起OOM異常
從以下幾個角度分析任務的特性:
任務的性質:CPU 密集型任務、IO 密集型任務和混合型任務。
任務的優先級:高、中、低。
任務的執行時間:長、中、短。
任務的依賴性:是否依賴其他系統資源,如數據庫連接。
任務性質不同的任務可以用不同規模的線程池分開處理??梢酝ㄟ^ Runtime.getRuntime().availableProcessors()方法獲得當前設備的 CPU 個數。
CPU 密集型任務:配置盡可能小的線程,如配置 cpu核心數+1 個線程的線程池。
IO 密集型任務 :由于線程并不是一直在執行任務,則配置盡可能多的線程,如2 ∗ Ncpu。
混合型任務:如果可以拆分,則將其拆分成一個 CPU 密集型任務和一個 IO 密集型任務。只要這兩個任務執行的時間相差不是太大,那么分解后執行的吞吐率要高于串行執行的吞吐率;如果這兩個任務執行時間相差太大,則沒必要進行分解。
優先級不同的任務可以使用優先級隊列 PriorityBlockingQueue 來處理,它可以讓優先級高的任務先得到執行。但是,如果一直有高優先級的任務加入到阻塞隊列中,那么低優先級的任務可能永遠不能執行。
執行時間不同的任務可以交給不同規模的線程池來處理,或者也可以使用優先級隊列,讓執行時間短的任務先執行。
依賴數據庫連接池的任務,因為線程提交 SQL 后需要等待數據庫返回結果,線程數應該設置得較大,這樣才能更好的利用 CPU。
建議使用有界隊列,有界隊列能增加系統的穩定性和預警能力??梢愿鶕枰O大一點,比如幾千。使用無界隊列,線程池的隊列就會越來越大,有可能會撐滿內存,導致整個系統不可用。
處理拒絕策略有以下幾種比較推薦:
在程序中捕獲RejectedExecutionException異常,在捕獲異常中對任務進行處理。針對默認拒絕策略使用CallerRunsPolicy拒絕策略,該策略會將任務交給調用execute的線程執行【一般為主線程】,此時主線程將在一段時間內不能提交任何任務,從而使工作線程處理正在執行的任務。此時提交的線程將被保存在TCP隊列中,TCP隊列滿將會影響客戶端,這是一種平緩的性能降低自定義拒絕策略,只需要實現RejectedExecutionHandler接口即可如果任務不是特別重要,使用DiscardPolicy和DiscardOldestPolicy拒絕策略將任務丟棄也是可以的如果使用Executors的靜態方法創建ThreadPoolExecutor對象,可以通過使用Semaphore對任務的執行進行限流也可以避免出現OOM異常。
有以下幾種比較推薦:
在程序中捕獲RejectedExecutionException異常,在捕獲異常中對任務進行處理。針對默認拒絕策略
使用CallerRunsPolicy拒絕策略,該策略會將任務交給調用execute的線程執行【一般為主線程】,此時主線程將在一段時間內不能提交任何任務,從而使工作線程處理正在執行的任務。此時提交的線程將被保存在TCP隊列中,TCP隊列滿將會影響客戶端,這是一種平緩的性能降低
自定義拒絕策略,只需要實現RejectedExecutionHandler接口即可
如果任務不是特別重要,使用DiscardPolicy和DiscardOldestPolicy拒絕策略將任務丟棄也是可以的如果使用Executors的靜態方法創建ThreadPoolExecutor對象,可以通過使用Semaphore對任務的執行進行限流也可以避免出現OOM異常。
線程狀態:

不同于線程狀態,線程池也有如下幾種 狀態:

• RUNNING :該狀態的線程池既能接受新提交的任務,又能處理阻塞隊列中任務。
• SHUTDOWN:該狀態的線程池不能接收新提交的任務,但是能處理阻塞隊列中的任務。(政府服務大廳不在允許群眾拿號了,處理完手頭的和排隊的政務就下班)
處于 RUNNING 狀態時,調用 shutdown()方法會使線程池進入到該狀態。
注意:finalize() 方法在執行過程中也會隱式調用shutdown()方法。
• STOP:該狀態的線程池不接受新提交的任務,也不處理在阻塞隊列中的任務,還會中斷正在執行的任務。(政府服務大廳不再進行服務了,拿號、排隊、以及手頭工作都停止了。)
在線程池處于 RUNNING 或 SHUTDOWN 狀態時,調用shutdownNow() 方法會使線程池進入到該狀態;
• TIDYING:如果所有的任務都已終止,workerCount (有效線程數)=0。
線程池進入該狀態后會調用 terminated() 鉤子方法進入TERMINATED 狀態。
• TERMINATED:在terminated()鉤子方法執行完后進入該狀態,默認terminated()鉤子方法中什么也沒有做。
關于“Java線程池知識點有哪些”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。