成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

漫畫:聊聊線程池中,線程的增長/回收策略

開發
我們今天就來借這個問題,聊聊線程池中維護的線程,它增長和回收的策略是什么樣的?

 一、序

 

 

public static ExecutorService newThreadPool() { return new ThreadPoolExecutor( 30, 60, 60L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());}

 

 

 

 

我們今天就來借這個問題,聊聊線程池中維護的線程,它增長和回收的策略是什么樣的?

二、線程池的策略

2.1 線程池的各項參數

當我們聊到線程池中線程的增長策略的時候,最抓眼球的就是它的核心線程數(corePoolSize)和最大線程數(maximumPoolSize),但是僅看這兩個參數是不夠全面的,線程數量的增長,還與任務等待隊列有關系。

我們先來看看 ThreadPoolExecutor 最全參數的構造方法:

  1. public ThreadPoolExecutor( 
  2.   int corePoolSize, 
  3.   int maximumPoolSize, 
  4.   long keepAliveTime, 
  5.   TimeUnit unit, 
  6.   BlockingQueue<Runnable> workQueue, 
  7.   ThreadFactory threadFactory, 
  8.   RejectedExecutionHandler handler) { 
  9.   // ... 

簡單解釋一下各個參數是什么意思:

  • corePoolSize:核心線程數;
  • maximumPoolSize:線程池的最大線程數;
  • keepAliveTime:核心線程數之外的線程,最大空閑存活的時長;
  • unit:keepAliveTime 的時間單位;
  • workQueue:線程池的任務等待隊列;
  • threadFractory:線程工廠,用來為線程池創建線程;
  • handler:拒絕策略,當線程池無法處理任務時的拒絕方式;

這其中很多參數的配置,都是相互影響的。例如任務等待隊列 workQueue 配置不當,可能導致線程池中的線程,永遠無法增長到核心線程數(maximumPoolSize)配置的線程數。

2.2 線程池中線程的增長策略

看到這里你應該就清楚了,線程池線程的增長策略,和 3 個參數有關系:

  • corePoolSize:核心線程數
  • maximumPoolSize:最大線程數;
  • workQueue:等待任務隊列;

它們之前的關系是這樣的:

 

 

 

 

接下來我們看看理想情況下,線程池中線程的增長策略。

默認情況下,初始時線程池是空的,當有新任務來了時,線程池開始通過線程工廠(threadFractory)創建線程來處理任務。

新的任務會不斷的觸發線程池中線程的創建,直到線程數量達到核心線程數(corePoolSize),接下來會停止線程的創建,而是將這個新任務放入任務等待隊列(workQueue)。

新任務不斷進入任務等待隊列,當該隊列滿了時,開始重新創建線程處理任務,直到線程池中線程的數量,到達 maximumPoolSize 配置的數量。

到這一步時,線程池的線程數達到最大值,并且沒有空閑的線程,任務隊列也存滿了任務,這時如果還有新的任務進來,就會觸發線程池的拒絕策略(handler),如果沒有配置拒絕策略就會拋出 RejectedExecutionException 異常。

到這里線程的增長策略就說清楚了,我們可以通過下圖來了解完整的流程。

 

 

 

 

其中比較關鍵的就是任務的等待隊列,無論等待隊列的實現結構是什么樣的,只有在它滿的時候,線程池中的線程才會向最大線程數增長。但是一個能夠滿的隊列,它的前提是必須是一個有界隊列。

這就是文章開頭舉的例子暗藏的坑,我們回顧一下前面構造的線程池。

  1. public static ExecutorService newThreadPool() { 
  2.   return new ThreadPoolExecutor( 
  3.     30, 60, 
  4.     60L, TimeUnit.MILLISECONDS, 
  5.     new LinkedBlockingQueue<Runnable>()); 

可以看到,這里雖然最大線程數是大于核心線程數的,但是它的等待隊列配置的是一個 LinkedBlockingQueue,從名字上可以看出這是一個基于鏈表實現的阻塞隊列,而用它的默認構造方法構造時,其容量設定為 Integer.MAX_VALUE,可以簡單理解它是一個無界隊列。

  1. public LinkedBlockingQueue() { 
  2.   this(Integer.MAX_VALUE); 
  3.  
  4. public LinkedBlockingQueue(int capacity) { 
  5.   if (capacity <= 0) throw new IllegalArgumentException(); 
  6.   this.capacity = capacity; 
  7.   last = head = new Node<E>(null); 

這也就是為什么說,這樣構造的線程池,核心線程數的配置參數,永遠都用不到,因為它的等待隊列永遠沒有滿的時候。

2.3 線程池中線程的收縮策略

線程池中執行的任務,總有執行結束的時候。那么線程池當線程池中存在大量空閑線程時,也會有一定的收縮策略,來回收線程池中多余的線程。

線程池中線程的收縮策略,和以下幾個參數相關:

  • corePoolSize:核心線程數;
  • maximumPoolSize:線程池的最大線程數;
  • keepAliveTime:核心線程數之外的線程,空閑存活的時長;
  • unit:keepAliveTime 的時間單位;

corePoolSize 和 maximumPoolSize 我們比較熟悉了,另外能夠控制它的就是 keepAliveTime 空閑存活時長,以及這個時長的單位。

當線程池中的線程數,超過核心線程數時。此時如果任務量下降,肯定會出現有一些線程處于無任務執行的空閑狀態。那么如果這個線程的空閑時間超過了 keepAliveTime&unit 配置的時長后,就會被回收。

需要注意的是,對于線程池來說,它只負責管理線程,對于創建的線程是不區分所謂的「核心線程」和「非核心線程」的,它只對線程池中的線程總數進行管理,當回收的線程數達到 corePoolSize 時,回收的過程就會停止。

對于線程池的核心線程數中的線程,也有回收的辦法,可以通過 allowCoreThreadTimeOut(true) 方法設置,在核心線程空閑的時候,一旦超過 keepAliveTime&unit 配置的時間,也將其回收掉。

 

  1. public void allowCoreThreadTimeOut(boolean value) { 
  2.   if (value && keepAliveTime <= 0) 
  3.     throw new IllegalArgumentException("Core threads must have nonzero keep alive times"); 
  4.   if (value != allowCoreThreadTimeOut) { 
  5.     allowCoreThreadTimeOut = value; 
  6.     if (value) 
  7.       interruptIdleWorkers(); 
  8.   } 

allowCoreThreadTimeOut() 能被設置的前提是 keepAliveTime 不能為 0。

2.3 查缺補漏

1. 等待隊列還會影響拒絕策略

等待隊列如果配置成了無界隊列,不光影響線程數量從核心線程數向最大線程數的增長,還會導致配置的拒絕策略永遠得不到執行。

因為只有在線程池中的工作線程數量已經達到核心線程數,并且此時等待隊列也滿了的情況下,拒絕策略才能生效。

2. 核心線程數可以被「預熱」

前面提到默認的情況下,線程池中的線程是根據任務來增長的。但如果有需要,我們也可以提前準備好線程池的核心線程,來應對突然的高并發任務,例如在搶購系統中就經常有這樣的需要。

此時就可以利用 prestartCoreThread() 或者 prestartAllCoreThreads() 來提前創建核心線程,這種方式被我們稱為「預熱」。

3. 對于需要無界隊列的場景,怎么辦?

需求是多變的,我們肯定會碰到需要使用無界隊列的場景,那么這種場景下配置的 maximumPoolSize 就是無效的。

此時就可以參考 Executors 中 newFixedThreadPool() 創建線程池的過程,將 corePoolSize 和 maximumPoolSize 保持一致即可。

  1. public static ExecutorService newFixedThreadPool(int nThreads) { 
  2.   return new ThreadPoolExecutor( 
  3.     nThreads, nThreads, 
  4.     0L, TimeUnit.MILLISECONDS, 
  5.     new LinkedBlockingQueue<Runnable>()); 

此時核心線程數就是最大線程數,只有增長到這個數量才會將任務放入等待隊列,來保證我們配置的線程數量都得到了使用。

4. 線程池是公平的嗎?

所謂的公平,就是先到的任務會被先執行。這在線程池中,顯然是不公平的。

不提線程池中線程執行任務是通過系統去調度的,這一點就決定了任務的執行順序是無法保證的,這就是是非公平的。另外只從線程池本身的角度來看,我們只看提交的任務順序來看,它也是非公平的。

首先前面到的任務,如果線程池的核心線程已經分配出去了,此時這個任務就會進入任務隊列,那么如果任務隊列滿了之后,新到的任務會直接由線程池新創建線程去處理,直到線程數達到最大線程數。

那么此時,任務隊列中的任務,雖然先添加進線程池等待處理,但是這些任務的處理時機,是晚于后續新創建線程去處理的任務的,所以說僅從任務的角度,依然是非公平的。

三、小結時刻

本文我們聊到了線程池中,對于線程數量的增長和收縮策略。

在這里我們簡單總結一下:

1. 增長策略。默認情況下,線程池是根據任務先創建足夠核心線程數的線程去執行任務,當核心線程滿了時將任務放入等待隊列。待隊列滿了的時候,繼續創建新線程執行任務直到到達最大線程數停止。再有新任務的話,那就只能執行拒絕策略或是拋出異常。

2. 收縮策略。當線程池線程數量大于核心線程數 && 當前有空閑線程 && 空閑線程的空閑時間大于 keepAliveTime 時,會對該空閑線程進行回收,直到線程數量等于核心線程數為止。

總之要謹記,慎用無界隊列。

責任編輯:武曉燕 來源: 承香墨影
相關推薦

2024-10-11 16:57:18

2024-04-02 09:53:08

線程池線程堆棧

2020-12-28 08:03:26

多線程進程瀏覽器

2024-04-08 10:09:37

TTLJava框架

2023-02-02 08:56:25

線程池線程submit

2024-08-29 08:54:35

2024-06-13 09:30:33

Java線程池線程

2023-04-02 17:53:10

多線程編程自測

2025-02-05 14:28:19

2024-03-12 13:11:20

powerjob單機線程

2024-12-02 10:04:04

2021-03-11 00:07:30

線程Thread程序

2022-02-07 11:55:00

linux進程線程

2022-06-07 23:28:05

線程安全后端

2023-10-26 08:25:35

Java線程周期

2021-02-01 08:28:24

Linux線程池Linux系統

2022-08-29 09:06:43

hippo4j動態線程池

2024-10-21 16:59:37

C#編程多線程

2024-06-04 07:52:04

2021-11-29 10:55:11

線程池Java面試
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 欧美激情久久久 | 在线色网站 | 久久国产精品免费 | 国内自拍第一页 | 婷婷福利 | 日韩欧美国产成人一区二区 | 亚洲精品电影在线观看 | 免费福利视频一区二区三区 | 欧美大片久久久 | 亚洲精选一区二区 | 综合五月婷| 亚洲国产一区二区视频 | 成人免费观看男女羞羞视频 | 日韩在线精品强乱中文字幕 | 日韩喷潮 | 丁香久久 | 日本三级电影在线看 | 日本高清视频在线播放 | 亚洲一二三视频 | 亚洲福利网站 | 欧美精品在线免费观看 | 国产成人免费视频网站视频社区 | 久久久久资源 | 日韩精品在线一区二区 | 国产1区| 狠狠干美女 | 欧美h版 | 久久丁香| 九色av| 国产精品一区二区三区久久久 | 91免费视频 | 欧美亚洲国产一区 | 日日夜夜草 | 欧美aaa | 一级黄色片网站 | 激情网站 | 最近最新中文字幕 | 日韩激情在线 | 日韩欧美一区二区三区 | 国产一区二区黑人欧美xxxx | 国产1区2区在线观看 |