線程池
- 線程池
-
- 為什麼要使用線程池?
- 接口
-
- Executors 提供四種線程池:
- 線程池相關參數的概念:
- 線程池的工作流程圖
- 線程池的關閉
- 問題
線程池
線程池,其實就是一個容納多個線程的容器,其中的線程可以反複使用,省去了頻繁建立線程對象的操作,無需反複建立線程而消耗過多資源。
為什麼要使用線程池?
在java中,如果每個請求到達就建立一個新線程,開銷是相當大的。在實際使用中,建立和銷毀線程花費的時間和消耗的系統資源都相當大,甚至可能要比在處理實際的使用者請求的時間和資源要多的多。除了建立和銷毀線程的開銷之外,活動的線程也需要消耗系統資源。如果在一個jvm裡建立太多的線程,可能會使系統由于過度消耗記憶體或“切換過度”而導緻系統資源不足。為了防止資源不足,需要采取一些辦法來限制任何給定時刻處理的請求數目,盡可能減少建立和銷毀線程的次數,特别是一些資源耗費比較大的線程的建立和銷毀,盡量利用已有對象來進行服務。
接口
Java裡面線程池的頂級接口是 Executor,不過真正的線程池接口是 ExecutorService, ExecutorService 的預設實作是 ThreadPoolExecutor;
Executors:線程池建立工廠類
普通類 Executors 裡面調用的就是 ThreadPoolExecutor。
Executors 類提供工廠方法用來建立不同類型的線程池。比如: newSingleThreadExecutor() 建立一個隻有一個線程的線程池,newFixedThreadPool(int numOfThreads)來建立固定線程數的線程池,newCachedThreadPool()可以根據需要建立新的線程,但如果已有線程是空閑的會重用已有線程。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL1IESjZnQzgVNCh0Y2J0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLyADN1YTY5ADZjRGZlBDNhBjYxQjN0czYiBDM4QmN3IzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
1、在建立了線程池後,線程池中的線程數為零。
2、當調用 execute()方法添加一個請求任務時,線程池會做出如下判斷:
2.1 如果正在運作的線程數量小于 corePoolSize,那麼馬上建立線程運作這個任務;
2.2 如果正在運作的線程數量大于或等于 corePoolSize,那麼将這個任務放入隊列;
2.3 如果這個時候隊列滿了且正在運作的線程數量還小于maximumPoolSize,那麼還是要建立非核心線程立刻運作這個任務;
2.4 如果隊列滿了且正在運作的線程數量大于或等于 maximumPoolSize,那麼線程池會啟動飽和拒絕政策來執行。
當送出的任務數大于(workQueue.size() +maximumPoolSize ),就會觸發線程池的拒絕政策。
3、當一個線程完成任務時,它會從隊列中取下一個任務來執行。
4、當一個線程無事可做超過一定的時間(keepAliveTime)時,線程會判斷:
4.1 如果目前運作的線程數大于 corePoolSize,那麼這個線程就被停掉。
4.2 是以線程池的所有任務完成後,它最終會收縮到 corePoolSize 的大小。
Executors 提供四種線程池:
1)newSingleThreadExecutor
線程池根據需求建立線程,可擴容,遇強則強(銀行一共10個視窗,隻開放了3個視窗,其他7個視窗沒有開放,如果人很多就要開放其餘的視窗,高峰結束再回複到3個視窗狀态)
建立一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則建立線程。
特點:
1、線程池中數量沒有固定,可達到最大值(Interger. MAX_VALUE)
2、線程池中的線程可進行緩存重複利用和回收(回收預設時間為 1 分鐘)
3、當線程池中,沒有可用線程,會重新建立一個線程
場景: 适用于建立一個可無限擴大的線程池,伺服器負載壓力較輕,執行時間較短,任務多的場景。
2)newFixedThreadPool
一個任務一個任務執行,一池一線程(例如視窗隻能服務一個人,後面來的隻能等待)
建立是一個單線程池,也就是該線程池隻有一個線程在工作,所有的任務是串行執行的,如果這個唯一的線程因為異常結束,那麼會有一個新的線程來替代它,
此線程池保證所有任務的執行順序按照任務的送出順序執行。
特點: 線程池中最多執行 1 個線程,之後送出的線程活動将會排在隊列中以此執行
場景: 适用于需要保證順序執行各個任務,并且在任意時間點,不會同時有多個線程的場景。
3)newFixedThreadPool (int) 一池N線程
建立固定大小的線程池,每次送出一個任務就建立一個線程,直到線程達到線程池的最大大小,線程池的大小一旦達到最大值就會保持不變。
特征:
1、線程池中的線程處于一定的量,可以很好的控制線程的并發量
2、線程可以重複被使用,在顯示關閉之前,都将一直存在
3、超出一定量的線程被送出時候需在隊列中等待
場景: 适用于可以預測線程數量的業務中,或者伺服器負載較重,對線程數有嚴格限制的場景
4)newScheduledThreadPool (了解)
建立一個大小無限的線程池,此線程池支援定時以及周期性執行任務的需求。
線程池相關參數的概念:
public threadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit timeUnit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory){
this(corePoolSize,maximumPoolSize,keepAliveTime,
timeUnit, workQueue,threadFactory,defaultHandler);
}
1)corePoolSize:3
線程池的核心線程數(常駐線程數)
一般情況下不管有沒有任務都會一直線上程池中一直存活
2)maximumPoolSize: 7
線程池所能容納的最大線程數,當活動的線程數達到這個值後,後續的新任務将會被阻塞。
3)keepAliveTime:4
線程閑置時的逾時時長
控制線程閑置時的逾時時長,超過則終止該線程。一般情況下用于非核心線程
4)unit:
用于指定 keepAliveTime 參數的時間機關,TimeUnit 是個 enum 枚舉類型,常用的有:TimeUnit.HOURS(小時)、TimeUnit.MINUTES(分鐘)、TimeUnit.SECONDS(秒) 和 TimeUnit.MILLISECONDS(毫秒)等。
5)workQueue:任務隊列(阻塞隊列)
當核心線程數達到最大時,新任務會放在隊列中排隊等待執行。
6)threadFactory:線程工廠
線程工廠,它是一個接口,用來為線程池建立新線程的。
7)RejectedExecutionHandler handler: 拒絕政策(銀行有10個視窗,核心是3個視窗,所有視窗都開放,等待的座位也坐滿了,銀行再來新的顧客,銀行沒有能力接受新的顧客,銀行就要做一個拒絕政策,建議去别的銀行)
線程池的工作流程圖
線程池的關閉
ThreadPoolExecutor 提供了兩個方法,用于線程池的關閉,分别是 shutdown() 和 shutdownNow()。
shutdown():不會立即的終止線程池,而是要等所有任務緩存隊列中的任務都執行完後才終止,但再也不會接受新的任務。
shutdownNow():立即終止線程池,并嘗試打斷正在執行的任務,并且清空任務緩存隊列,傳回尚未執行的任務。
問題
1、什麼是 Executor 架構?
Executor架構在Java 5中被引入,Executor 架構是一個根據一組執行政策調用、排程、執行和控制的異步任務的架構。
無限制的建立線程會引起應用程式記憶體溢出,是以建立一個線程池是個更好的的解決方案,因為可以限制線程的數量并且可以回收再利用這些線程。利用 Executor 架構可以非常友善的建立一個線程池。
2、Executors 類是什麼?
Executors為Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 類提供了一些工具方法。Executors 可以用于友善的建立線程池。