1. 程式人生 > >java執行緒池引數說明及佇列拒絕策略

java執行緒池引數說明及佇列拒絕策略

 java.util.concurrent.ThreadPoolExecutor,其構造方法1:

複製程式碼
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {
        
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), handler); }
複製程式碼
  • corePoolSize: 執行緒池維護執行緒的最少數量
  • maximumPoolSize:執行緒池維護執行緒的最大數量
  • keepAliveTime: 執行緒池維護執行緒所允許的空閒時間
  • unit: 執行緒池維護執行緒所允許的空閒時間的單位
  • workQueue: 執行緒池所使用的緩衝佇列
  • handler: 執行緒池對拒絕任務的處理策略


一個任務通過 execute(Runnable)方法被新增到執行緒池,任務就是一個 Runnable型別的物件,任務的執行方法就是 Runnable型別物件的run()方法。

當一個任務通過execute(Runnable)方法欲新增到執行緒池時:

  1. 如果此時執行緒池中的數量小於corePoolSize,即使執行緒池中的執行緒都處於空閒狀態,也要建立新的執行緒來處理被新增的任務。
  2. 如果此時執行緒池中的數量等於 corePoolSize,但是緩衝佇列 workQueue未滿,那麼任務被放入緩衝佇列。
  3. 如果此時執行緒池中的數量大於corePoolSize,緩衝佇列workQueue滿,並且執行緒池中的數量小於maximumPoolSize,建新的執行緒來處理被新增的任務。
  4. 如果此時執行緒池中的數量大於corePoolSize,緩衝佇列workQueue滿,並且執行緒池中的數量等於maximumPoolSize,那麼通過 handler所指定的策略來處理此任務。也就是:處理任務的優先順序為:核心執行緒corePoolSize、任務佇列workQueue、最大執行緒maximumPoolSize,如果三者都滿了,使用handler處理被拒絕的任務。
  5. 當執行緒池中的執行緒數量大於 corePoolSize時,如果某執行緒空閒時間超過keepAliveTime,執行緒將被終止。這樣,執行緒池可以動態的調整池中的執行緒數。

workQueue常用的是:java.util.concurrent.ArrayBlockingQueue

handler有四個選擇:

  • ThreadPoolExecutor.AbortPolicy()              ---   丟擲java.util.concurrent.RejectedExecutionException異常
  • ThreadPoolExecutor.CallerRunsPolicy()       ---   重試添加當前的任務,他會自動重複呼叫execute()方法
  • ThreadPoolExecutor.DiscardOldestPolicy()   ---   拋棄舊的任務
  • ThreadPoolExecutor.DiscardPolicy()            ---   拋棄當前的任務
——————————————————————————————————————————————————————————————————————

工作原理

1、執行緒池剛建立時,裡面沒有一個執行緒。任務佇列是作為引數傳進來的。不過,就算佇列裡面有任務,執行緒池也不會馬上執行它們。

2、當呼叫 execute() 方法新增一個任務時,執行緒池會做如下判斷:

       a. 如果正在執行的執行緒數量小於 corePoolSize,那麼馬上建立執行緒執行這個任務;

   b. 如果正在執行的執行緒數量大於或等於 corePoolSize,那麼將這個任務放入佇列。

   c. 如果這時候佇列滿了,而且正在執行的執行緒數量小於 maximumPoolSize,那麼還是要建立執行緒執行這個任務;

   d. 如果佇列滿了,而且正在執行的執行緒數量大於或等於 maximumPoolSize,那麼執行緒池會丟擲異常,告訴呼叫者“我不能再接受任務了”。

3、當一個執行緒完成任務時,它會從佇列中取下一個任務來執行。

4、當一個執行緒無事可做,超過一定的時間(keepAliveTime)時,執行緒池會判斷,如果當前執行的執行緒數大於 corePoolSize,那麼這個執行緒就被停掉。所以執行緒池的所有任務完成後,它最終會收縮到 corePoolSize 的大小。

   這樣的過程說明,並不是先加入任務就一定會先執行。假設佇列大小為 10,corePoolSize 為 3,maximumPoolSize 為 6,那麼當加入 20 個任務時,執行的順序就是這樣的:首先執行任務 1、2、3,然後任務 4~13 被放入佇列。這時候佇列滿了,任務 14、15、16 會被馬上執行,而任務 17~20 則會丟擲異常。最終順序是:1、2、3、14、15、16、4、5、6、7、8、9、10、11、12、13。下面是一個執行緒池使用的例子:


排隊

所有 BlockingQueue 都可用於傳輸和保持提交的任務。可以使用此佇列與池大小進行互動:
  • 如果執行的執行緒少於 corePoolSize,則 Executor 始終首選新增新的執行緒,而不進行排隊。
  • 如果執行的執行緒等於或多於 corePoolSize,則 Executor 始終首選將請求加入佇列,而不新增新的執行緒。
  • 如果無法將請求加入佇列,則建立新的執行緒,除非建立此執行緒超出 maximumPoolSize,在這種情況下,任務將被拒絕。
排隊有三種通用策略:
  1. 直接提交。工作佇列的預設選項是 SynchronousQueue,它將任務直接提交給執行緒而不保持它們。在此,如果不存在可用於立即執行任務的執行緒,則試圖把任務加入佇列將失敗,因此會構造一個新的執行緒。此策略可以避免在處理可能具有內部依賴性的請求集時出現鎖。直接提交通常要求無界 maximumPoolSizes 以避免拒絕新提交的任務。當命令以超過佇列所能處理的平均數連續到達時,此策略允許無界執行緒具有增長的可能性。
  2. 無界佇列。使用無界佇列(例如,不具有預定義容量的 LinkedBlockingQueue)將導致在所有 corePoolSize 執行緒都忙時新任務在佇列中等待。這樣,建立的執行緒就不會超過 corePoolSize。(因此,maximumPoolSize 的值也就無效了。)當每個任務完全獨立於其他任務,即任務執行互不影響時,適合於使用無界佇列;例如,在 Web 頁伺服器中。這種排隊可用於處理瞬態突發請求,當命令以超過佇列所能處理的平均數連續到達時,此策略允許無界執行緒具有增長的可能性。
  3. 有界佇列。當使用有限的 maximumPoolSizes 時,有界佇列(如 ArrayBlockingQueue)有助於防止資源耗盡,但是可能較難調整和控制。佇列大小和最大池大小可能需要相互折衷:使用大型佇列和小型池可以最大限度地降低 CPU 使用率、作業系統資源和上下文切換開銷,但是可能導致人工降低吞吐量。如果任務頻繁阻塞(例如,如果它們是 I/O 邊界),則系統可能為超過您許可的更多執行緒安排時間。使用小型佇列通常要求較大的池大小,CPU 使用率較高,但是可能遇到不可接受的排程開銷,這樣也會降低吞吐量。
被拒絕的任務當 Executor 已經關閉,並且 Executor 將有限邊界用於最大執行緒和工作佇列容量,且已經飽和時,在方法 execute(java.lang.Runnable) 中提交的新任務將被拒絕。在以上兩種情況下,execute 方法都將呼叫其RejectedExecutionHandler 的RejectedExecutionHandler.rejectedExecution(java.lang.Runnable, java.util.concurrent.ThreadPoolExecutor) 方法。下面提供了四種預定義的處理程式策略:

定義和使用其他種類的 RejectedExecutionHandler 類也是可能的,但這樣做需要非常小心,尤其是當策略僅用於特定容量或排隊策略時。 

公用的類ThreadPoolTask

  1. publicclass ThreadPoolTask implements Runnable {   
  2.   // 儲存任務所需要的資料 
  3.   private Object threadPoolTaskData;   
  4.   privatestaticint consumerTaskSleepTime = 2000;   
  5.   ThreadPoolTask(Object tasks) {   
  6.     this.threadPoolTaskData = tasks;   
  7.   }   
  8.   publicvoid run() {   
  9.     // 處理一個任務,這裡的處理方式太簡單了,僅僅是一個列印語句 
  10.     System.out.println("start .." + threadPoolTaskData);   
  11.     try {   
  12.       //便於觀察,等待一段時間 
  13.       Thread.sleep(consumerTaskSleepTime);   
  14.     } catch (Exception e) {   
  15.       e.printStackTrace();   
  16.     }   
  17.     System.out.println("finish " + threadPoolTaskData);   
  18.     threadPoolTaskData = null;   
  19.   }   
  20. }   


  1. import java.util.concurrent.ArrayBlockingQueue;   
  2. import java.util.concurrent.LinkedBlockingQueue;  
  3. import java.util.concurrent.ThreadPoolExecutor;   
  4. import java.util.concurrent.TimeUnit;   
  5. publicclass ThreadPool {   
  6.   //讓可執行程式休息一下
  7.   privatestaticint executePrograms = 0;   
  8.   privatestaticint produceTaskMaxNumber = 10;   
  9.   publicstaticvoid main(String[] args) {   
  10.     // 構造一個執行緒池 
  11.     ThreadPoolExecutor threadPool = new ThreadPoolExecutor(243,   
  12.         TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(3),   
  13.         new ThreadPoolExecutor.CallerRunsPolicy());   
  14.     for (int i = 1; i <= produceTaskMaxNumber; i++) {   
  15.       try {   
  16.         String task = "[email protected] " + i;   
  17.         System.out.println("put " + task);   
  18.         threadPool.execute(new ThreadPoolTask(task));   
  19.         // 便於觀察,等待一段時間 
  20.         Thread.sleep(executePrograms);   
  21.       } catch (Exception e) {   
  22.         e.printStackTrace();   
  23.       }   
  24.     }   
  25.   }   
  26. }   
執行結果
  1. put [email protected] 1
  2. put [email protected] 2
  3. start [email protected] 1
  4. put [email protected] 3
  5. put [email protected] 4
  6. start [email protected] 2
  7. put [email protected] 5
  8. put [email protected] 6
  9. put [email protected] 7
  10. start [email protected] 6
  11. put [email protected] 8
  12. start [email protected] 7
  13. java.util.concurrent.RejectedExecutionException  
  14.     at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(Unknown Source)  
  15.     at java.util.concurrent.ThreadPoolExecutor.reject(Unknown Source)  
  16.     at java.util.concurrent.ThreadPoolExecutor.execute(Unknown Source)  
  17.     at ThreadPool.main(ThreadPool.java:22)  
  18. put [email protected] 9
  19. java.util.concurrent.RejectedExecutionException  
  20.     at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(Unknown Source)  
  21.     at java.util.concurrent.ThreadPoolExecutor.reject(Unknown Source)  
  22.     at java.util.concurrent.ThreadPoolExecutor.execute(Unknown Source)  
  23.     at ThreadPool.main(ThreadPool.java:22)  
  24. put [email protected] 10
  25. java.util.concurrent.RejectedExecutionException  
  26.     at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(Unknown Source)  
  27.     at java.util.concurrent.ThreadPoolExecutor.reject(Unknown Source)  
  28.     at java.util.concurrent.ThreadPoolExecutor.execute(Unknown Source)  
  29.     at ThreadPool.main(ThreadPool.java:22)  
  30. finish [email protected] 2
  31. finish [email protected] 6
  32. finish [email protected] 7
  33. start [email protected] 4
  34. start [email protected] 3
  35. finish [email protected] 1
  36. start [email protected] 5
  37. finish [email protected] 4
  38. finish [email protected] 3
  39. finish [email protected] 5


  1. import java.util.concurrent.ArrayBlockingQueue;   
  2. import java.util.concurrent.LinkedBlockingQueue;  
  3. import java.util.concurrent.ThreadPoolExecutor;   
  4. 相關推薦

    java執行引數說明佇列拒絕策略

     java.util.concurrent.ThreadPoolExecutor,其構造方法1: public ThreadPoolExecutor(int corePoolSize, int maximumP

    Java執行引數詳解

    JDK1.5中引入了強大的concurrent包,其中最常用的莫過了執行緒池的實現ThreadPoolExecutor,它給我們帶來了極大的方便,但同時,對於該執行緒池不恰當的設定也可能使其效率並不能達到預期的效果,甚至僅相當於或低於單執行緒的效率。 ThreadPoolExecutor類可設定

    Java執行詳解例項

    前言 多執行緒的非同步執行方式,雖然能夠最大限度發揮多核計算機的計算能力,但是如果不加控制,反而會對系統造成負擔。執行緒本身也要佔用記憶體空間,大量的執行緒會佔用記憶體資源並且可能會導致Out of Memory。即便沒有這樣的情況,大量的執行緒回收也會給GC帶來很大的壓力

    java執行ThreadPoolExecutor和阻塞佇列BlockingQueue,Executor, ExecutorService

    ThreadPoolExecutor 引數最全的建構函式 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,

    Java - 執行引數

    ThreadPoolExecutor ThreadPoolExecutor是執行緒池的真正實現,他通過構造方法的一系列引數(不同的構造方法),來構成不同配置的執行緒池。   構造方法引數 corePoolSize 核心執行緒數,預設情況下核心執行緒會一直存活,即使處

    java 執行詳解四種執行用法介紹

    java 執行緒池詳解      Executor框架是一種將執行緒的建立和執行分離的機制。它基於Executor和ExecutorService介面,及這兩個介面的實現類ThreadPoolExecutor展開,Executor有一個內部執行緒池,並提供了將任務傳遞到池中

    java執行ThreadPoolExecutor原理使用

    其構造方法為public class ThreadPoolExecutor extends AbstractExecutorService{<span style="white-space:pre"> </span><div class="li

    JAVA執行ThreadPoolExecutor與阻塞佇列BlockingQueue

    池技術是典型的享元模式。 頻繁使用new Thread來建立執行緒的方式並不太好。因為每次new Thread新建和銷燬物件效能較差,執行緒缺乏統一管理。好在java提供了執行緒池,它能夠有效的管理、排程執行緒,避免過多的資源消耗。優點如下: 重用

    Java執行原始碼解析高質量程式碼案例

    引言 本文為Java高階程式設計中的一些知識總結,其中第一章對Jdk 1.7.0_25中的多執行緒架構中的執行緒池ThreadPoolExecutor原始碼進行架構原理介紹以及原始碼解析。第二章則分析了幾個違反Java高質量程式碼案例以及相應解決辦法。如有總結

    Java 執行詳解例項程式碼

    這篇文章主要介紹了Java 執行緒池的相關資料,並符例項程式碼,幫助大家學習參考,需要的朋友可以參考下執行緒池的技術背景在面向物件程式設計中,建立和銷燬物件是很費時間的,因為建立一個物件要獲取記憶體資源或者其它更多資源。在Java中更是如此,虛擬機器將試圖跟蹤每一個物件,以便

    Java執行詳解常用方法

    前言 最近被問到了執行緒池的相關問題。於是準備開始寫一些多執行緒相關的文章。這篇將介紹一下執行緒池的基本使用。 Executors Executors是concurrent包下的一個類,為我們提供了建立執行緒池的簡便方法。 Executors可以建立我們常用的四種執行緒池: (1)newCachedThrea

    執行ExecutorService的4種拒絕策略

    ThreadPoolExecutor.AbortPolicy:丟棄任務並丟擲RejectedExecutionException異常。 ThreadPoolExecutor.DiscardPolicy:也是丟棄任務,但是不丟擲異常。 ThreadPoolExecutor.Di

    Java執行原理使用

    java中的執行緒池是運用場景最多的併發框架。在開發過程中,合理的使用執行緒池能夠帶來下面的一些好處: 1、降低資源的消耗。 2、提高響應速度。 3、提高執行緒的可管理型。 1.1、執行緒池ThreadPoolExecutor工作原理 講解之前,我們先看一張原理

    (五)java 執行工作佇列

    執行緒池工作佇列   上一章我們介紹了執行緒的基本情況,這一章進一步瞭解執行緒池中的工作佇列,BlockingQueue 佇列。 在類 Executors 中,我們可以看到不同執行緒池維護的工作佇列是不同的,如newCachedThreadPool使用的是Sy

    java 執行 Executors ThreadPoolExecutor

    Java通過Executors提供四種執行緒池,分別為:  newCachedThreadPool建立一個可快取執行緒池,如果執行緒池長度超過處理需要,可靈活回收空閒執行緒,若無可回收,則新建執行緒。  newFixedThreadPool 建立一個定長執行緒池,可控制執行緒最大併發數,超出的執行緒會在佇列

    深入理解Java執行原理分析與使用(尤其當執行佇列滿了之後事項)

    在這裡借花獻佛了,那別人的東西學一學了。在我們的開發中“池”的概念並不罕見,有資料庫連線池、執行緒池、物件池、常量池等等。下面我們主要針對執行緒池來一步一步揭開執行緒池的面紗。使用執行緒池的好處1、降低資源消耗可以重複利用已建立的執行緒降低執行緒建立和銷燬造成的消耗。2、提高響應速度當任務到達時,任務可以不需

    Java執行Future、Callable獲得執行返回結果

    Java執行緒池及Future、Callable獲得執行緒返回結果【Java執行緒池系列2】 Java多執行緒程式設計中,經常使用的Thread的Runnable()雖然被經常使用,但其有一個弊端,就是因為無法直接獲取該執行緒的返回值,因為Runnable內的run方法,

    java執行之ThreadPoolExecutor(二):任務入佇列和任務丟棄

    一、關於任務入佇列 在上一篇【java執行緒池之ThreadPoolExecutor(一)】中, ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS,

    java執行介紹簡單使用舉例

    多執行緒雖然能夠提升程式的效能,但其實也是一把雙刃劍。"為每一個任務分配一個執行緒"的問題在於資源管理的複雜性。當我們需要頻繁的建立多個執行緒進行耗時操作時,每次通過new Thread來建立並不是一種好的辦法。new Thread 新建和銷燬物件的效能較差,執行緒缺乏統一

    Java執行ThreadPoolExecutor解析Executors類中提供的靜態方法來建立執行

    上面的程式碼可能看起來不是那麼容易理解,下面我們一句一句解釋:   首先,判斷提交的任務command是否為null,若是null,則丟擲空指標異常;   接著是這句,這句要好好理解一下: if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(c