1. 程式人生 > >回頭看JDK5中的concurrent包、執行緒池

回頭看JDK5中的concurrent包、執行緒池

本文轉載自:http://blog.csdn.net/sfdev/archive/2008/12/30/3648457.aspx

最近的Notiy系統終於快告一段落了,已經進入QA測試階段,真正編碼的那幾天時間真叫人懷念啊!已經好久沒有過這種暢快的感覺了,倒不是說平時完全沒機會編碼,主要是平時更多的涉及業務邏輯;

Notify系統中為了讓系統更輕量級,低耦合 ,對於併發操作的核心主要是基於JDK自帶的concurrent包,由Doug Lea捐贈;下面就回顧下這個包中的幾個重要類、以及重要方法、屬性等;
concurrent併發包裡面幾個重要的介面有:Executor、ExecutorService, ScheduledExecutorService


重要的實現類有:ScheduledThreadPoolExecutor, ThreadPoolExecutor
關於這幾個介面和實現類的類圖可以參見文件最後的UML圖,圖中對一些比較重要的屬性、方法進行紅色標識,可以重點關注;

先來說說java.util.concurrent.ThreadPoolExecutor ,也就是我們經常說到的執行緒池,通過該類,應用可以直接拿來使用,只要在初始化時設定不同的引數即可;其主要的引數有以下幾個:

  • corePoolSize : 執行緒池維護執行緒的最少數量
  • maximumPoolSize :執行緒池維護執行緒的最大數量
  • keepAliveTime
    : 執行緒池維護執行緒所允許的空閒時間
  • unit : 執行緒池維護執行緒所允許的空閒時間的單位
  • workQueue : 執行緒池所使用的緩衝佇列
  • handler : 執行緒池對拒絕任務的處理策略

一個任務通過execute(Runnable)方法被新增到執行緒池,任務就是一個 Runnable型別的物件,任務的執行方法就是Runnable型別物件的run()方法;注意:是Runnable,而不是Thread

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

  • 如果此時執行緒池中的數量小於corePoolSize,即使執行緒池中的執行緒都處於空閒狀態,也要建立新的執行緒來處理被新增的任務。
  • 如果此時執行緒池中的數量等於corePoolSize,但是緩衝佇列workQueue未滿,那麼任務被放入緩衝佇列。
  • 如果此時執行緒池中的數量大於corePoolSize,緩衝佇列workQueue滿,並且執行緒池中的數量小於maximumPoolSize,建新的執行緒來處理被新增的任務。
  • 如果此時執行緒池中的數量大於corePoolSize,緩衝佇列workQueue滿,並且執行緒池中的數量等於maximumPoolSize,那麼通過handler所指定的策略來處理此任務。
  • 從上面可以看出執行緒池中處理任務的優先順序為:
    核心執行緒corePoolSize、任務佇列workQueue、最大執行緒maximumPoolSize,如果三者都滿了,使用handler處理被拒絕的任務。

當執行緒池中的執行緒數量大於 corePoolSize時,如果某執行緒空閒時間超過keepAliveTime,執行緒將被終止。這樣,執行緒池可以動態的調整池中的執行緒數。

unit可選的引數為java.util.concurrent.TimeUnit中的幾個靜態屬性:
NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS。

預設handler有四個選擇,當然也可以自行擴充套件,但是要特別小心:

  • ThreadPoolExecutor.AbortPolicy:直接丟擲java.util.concurrent.RejectedExecutionException異常;
  • ThreadPoolExecutor.CallerRunsPolicy:主執行緒直接嘗試執行該任務;當執行緒池中可加入時,將任務新增到執行緒池中;該操作會重複執行,可以有效降低主執行緒將任務加入到執行緒池的速度;
  • ThreadPoolExecutor.DiscardOldestPolicy:直接拋棄舊的任務,即把執行緒池內最早加入佇列的執行緒拋棄;
  • ThreadPoolExecutor.DiscardPolicy:直接拋棄當前的任務;

再來說說java.util.concurrent.ScheduledThreadPoolExecutor ,此類是ThreadPoolExecutor的子類,所以以上我們描述的特性他都具備;除此之外,他還有一些自己特有的屬性和方法:

  • schedule(Runnable command, long delay, TimeUnit unit)
    該方法建立並執行在給定延遲後啟用的一次性任務;
  • scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
    該方法建立並執行一個在給定初始延遲後首次啟用的定期任務,後續任務具有指定的週期;也就是將在initialDelay後開始執行,然後在initialDelay+period 後執行,接著在initialDelay + 2 * period 後執行,依此類推。
  • scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
    該方法建立並執行一個在給定初始延遲後首次啟用的定期任務,隨後,在每一次執行終止和下一次執行開始之間都存在給定的延遲。

對於concurrent包在Spring中也進行了很多的封裝,對於一些可以採用FixedRate 或者FixedDelay 來進行排程的任務,非常的方便,相比較於Quartz的實現,在配置檔案方面要減少很多,有興趣的同學可以參考Spring文件中的《第23章 Spring中的定時排程和執行緒池》,重點關注兩個類:
org.springframework.scheduling.concurrent.ScheduledExecutorFactoryBean
org.springframework.scheduling.concurrent.ScheduledExecutorTask
org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor

附:concurrent包中幾個核心介面、類的UML類圖: