java並發編程之ThreadPoolExecutor

來源:互聯網
上載者:User

包 java.util.concurrent.*
[ 一 ]、常用線程池 

最常用構造方法為:

ThreadPoolExecutor(int corePoolSize,
                   int maximumPoolSize,
                   long keepAliveTime,
                   TimeUnit unit,
                   BlockingQueue<Runnable> workQueue,

                ThreadFactory threadFactory,

                   RejectedExecutionHandler handler)

 

JDK內建的配置好的線程池:

        // 固定背景工作執行緒數量的線程池
        ExecutorService executorService1 = Executors.newFixedThreadPool(3);

        // 一個可快取的線程池
        ExecutorService executorService2 = Executors.newCachedThreadPool();

        // 單線程化的Executor
        ExecutorService executorService3 = Executors.newSingleThreadExecutor();

        // 支援定時的以及周期性的任務執行
        ExecutorService executorService4 = Executors.newScheduledThreadPool(3);
這些預定義好的線程池服務也是基於ThreadPoolExecutor配置的,所以我們應該從最基本的參數著手瞭解,如下:

參數詳細說明:
[ 1 ]、corePoolSize: 線程池維護線程的最少數量
[ 2 ]、maximumPoolSize:線程池維護線程的最大數量
[ 3 ]、keepAliveTime: 線程池維護線程所允許的空閑時間
[ 4 ]、unit: 線程池維護線程所允許的空閑時間的單位,unit可選的參數為java.util.concurrent.TimeUnit中的幾個靜態屬性:

  • NANOSECONDS
  • MICROSECONDS
  • MILLISECONDS
  • SECONDS

[ 5]、 workQueue: 線程池所使用的緩衝隊列,常用的是:java.util.concurrent.ArrayBlockingQueue 

[6 ]    threadFactory 負責給線程池建立線程的工廠。線程池中的線程是由ThreadFactory建立。如果不特別指定,會使用Executors.defaultThreadFactory建立位於同一個線程組,相同優先順序(NORM_PRIORITY)的非守護線程。如果由你來指定ThreadFactory,你可以定製線程名字,線程組,優先順序,是否為守護線程等屬性。
[ 7 ]、 handler: 線程池對拒絕任務的處理策略,有四個選擇如下: 

  • ThreadPoolExecutor.AbortPolicy():拋出java.util.concurrent.RejectedExecutionException異常
  • ThreadPoolExecutor.CallerRunsPolicy():重試添加當前的任務,他會自動重複調用execute()方法
  • ThreadPoolExecutor.DiscardOldestPolicy():拋棄舊的任務
  • ThreadPoolExecutor.DiscardPolicy():拋棄當前的任務

[ 二 ]、詳細說明 
[ 1 ]、當一個任務通過execute(Runnable)方法欲添加到線程池時: 

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

 

ThreadPoolExecutor的工作機制: 

 

整個ThreadPoolExecutor的任務處理有4步操作:

 

  • 第一步,初始的poolSize < corePoolSize,提交的runnable任務,會直接做為new一個Thread的參數,立馬執行
  • 第二步,當提交的任務數超過了corePoolSize,就進入了第二步操作。會將當前的runable提交到一個block queue中
  • 第三步,如果block queue是個有界隊列,當隊列滿了之後就進入了第三步。如果poolSize < maximumPoolsize時,會嘗試new 一個Thread的進行救急處理,立馬執行對應的runnable任務
  • 第四步,如果第三步救急方案也無法處理了,就會走到第四步執行reject操作。
幾點說明:(相信這些網上一搜一大把,我這裡簡單介紹下,為後面做一下鋪墊)
  • block queue有以下幾種實現:
    1. ArrayBlockingQueue :  有界的數組隊列
    2. LinkedBlockingQueue : 可支援有界/無界的隊列,使用鏈表實現
    3. PriorityBlockingQueue : 優先隊列,可以針對任務排序
    4. SynchronousQueue : 隊列長度為1的隊列,和Array有點區別就是:client thread提交到block queue會是一個阻塞過程,直到有一個worker thread串連上來poll task。
  • RejectExecutionHandler是針對任務無法處理時的一些自保護處理:
    1. Reject 直接拋出Reject exception
    2. Discard 直接忽略該runnable,不可取
    3. DiscardOldest 丟棄最早入隊列的的任務
    4. CallsRun 直接讓原先的client thread做為worker線程,進行執行
 容易被人忽略的點:1.  pool threads啟動後,以後的任務擷取都會通過block queue中,擷取堆積的runnable task. 所以建議: block size >= corePoolSize ,不然線程池就沒任何意義2.  corePoolSize 和 maximumPoolSize的區別, 和大家正常理解的資料庫連接池不太一樣。  *  據dbcp pool為例,會有minIdle , maxActive配置。minIdle代表是常駐記憶體中的threads數量,maxActive代表是工作的最大線程數。  *  這裡的corePoolSize就是串連池的maxActive的概念,它沒有minIdle的概念(每個線程可以設定keepAliveTime,超過多少時間多有任務後銷毀線程,但不會固定保持一定數量的threads)。   * 這裡的maximumPoolSize,是一種救急措施的第一層。當threadPoolExecutor的工作threads存在滿負荷,並且block queue隊列也滿了,這時代表接近崩潰邊緣。這時允許臨時起一批threads,用來處理runnable,處理完後立馬退出。 所以建議:  maximumPoolSize >= corePoolSize =期望的最大線程數。 (我曾經配置了corePoolSize=1, maximumPoolSize=20, blockqueue為無界隊列,最後就成了單線程工作的pool。典型的配置錯誤) 3. 善用blockqueue和reject組合. 這裡要重點推薦下CallsRun的Rejected Handler,從字面意思就是讓調用者自己來運行。我們經常會線上上使用一些線程池做非同步處理,比如我前面做的(業務層)非同步並行載入技術分析和設計, 將原本串列的請求都變為了並行操作,但過多的並行會增加系統的負載(比如非強制中斷,環境切換)。所以肯定需要對線程池做一個size限制。但是為了引入非同步作業後,避免因在block queue的等待時間過長,所以需要在隊列滿的時,執行一個callsRun的策略,並行的操作又轉為一個串列處理,這樣就可以保證盡量少的延遲影響。 所以建議:  RejectExecutionHandler = CallsRun ,  blockqueue size = 2 * poolSize (為啥是2倍poolSize,主要一個考慮就是瞬間高峰處理,允許一個thread等待一個runnable任務) 參考:http://www.iteye.com/topic/1118660 更多:http://blog.csdn.net/waterbig/article/details/4794244http://rdc.taobao.com/team/jm/archives/595 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.