1. 程式人生 > >執行緒池ThreadPoolTaskExecutor配置說明

執行緒池ThreadPoolTaskExecutor配置說明

     一般實際開發中經常用到多執行緒,所以需要使用執行緒池了,

 ThreadPoolTaskExecutor通常通過XML方式配置,或者通過Executors的工廠方法進行配置。  XML方式配置程式碼如下:交給spring來管理;

  <bean id="taskExecutor"
          class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
        <!-- 核心執行緒數 -->
        <property name="corePoolSize" value="4000" />
        <!-- 最大執行緒數 -->
        <property name="maxPoolSize" value="20000" />
        <!-- 佇列最大長度 -->
        <property name="queueCapacity" value="2000" />
        <!-- 執行緒池維護執行緒所允許的空閒時間 -->
        <property name="keepAliveSeconds" value="30" />
        <!-- 執行緒池對拒絕任務(無執行緒可用)的處理策略 -->
        <property name="rejectedExecutionHandler">
            <bean class
="java.util.concurrent.ThreadPoolExecutor$DiscardPolicy" /> </property> </bean>

rejectedExecutionHandler欄位用於配置拒絕策略,常用的拒絕策略如下:

           AbortPolicy,用於被拒絕任務的處理程式,它將丟擲RejectedExecutionException。

          CallerRunsPolicy,用於被拒絕任務的處理程式,它直接在execute方法的呼叫執行緒中執行被拒絕的任務。

          DiscardOldestPolicy,用於被拒絕任務的處理程式,它放棄最舊的未處理請求,然後重試execute

          DiscardPolicy,用於被拒絕任務的處理程式,預設情況下它將丟棄被拒絕的任務。

提交任務

    無返回值的任務使用execute(Runnable)

    有返回值的任務使用submit(Runnable)

案例程式碼 

 threadPoolTaskExecutor.execute(new Runnable() {
                public void run() {
                    synchronized (Controller01.class) {
                        
try { HttpUtils.get("http://192.168.31.223:8085/test2.do"); System.out.println(System.currentTimeMillis()); } catch (Exception e) { e.printStackTrace(); } } } });

任務處理流程:

  • 當一個任務被提交到執行緒池時,首先檢視執行緒池的核心執行緒是否都在執行任務,否就選擇一條執行緒執行任務,是就執行第二步。
  • 檢視核心執行緒池是否已滿,不滿就建立一條執行緒執行任務,否則執行第三步。
  • 檢視任務佇列是否已滿,不滿就將任務儲存在任務佇列中,否則執行第四步。
  • 檢視執行緒池是否已滿,不滿就建立一條執行緒執行任務,否則就按照策略處理無法執行的任務。

在ThreadPoolExecutor中表現為:

  • 如果當前執行的執行緒數小於corePoolSize,那麼就建立執行緒來執行任務(執行時需要獲取全域性鎖)。
  • 如果執行的執行緒大於或等於corePoolSize,那麼就把task加入BlockQueue。
  • 如果建立的執行緒數量大於BlockQueue的最大容量,那麼建立新執行緒來執行該任務。
  • 如果建立執行緒導致當前執行的執行緒數超過maximumPoolSize,就根據飽和策略來拒絕該任務。

關閉執行緒池

        呼叫shutdown或者shutdownNow,

        兩者都不會接受新的任務,而且通過呼叫要停止執行緒的interrupt方法來中斷執行緒,有可能執行緒永遠不會被中斷,

       不同之處在於shutdownNow會首先將執行緒池的狀態設定為STOP,然後嘗試停止所有執行緒(有可能導致部分任務沒有執行完)然後返回未執行任務的列表。

       而shutdown則只是將執行緒池的狀態設定為shutdown,然後中斷所有沒有執行 

       任務的執行緒,並將剩餘的任務執行完。

常用狀態:

  • taskCount:執行緒需要執行的任務個數。
  • completedTaskCount:執行緒池在執行過程中已完成的任務數。
  • largestPoolSize:執行緒池曾經建立過的最大執行緒數量。
  • getPoolSize獲取當前執行緒池的執行緒數量。
  • getActiveCount:獲取活動的執行緒的數量

     通過繼承執行緒池,重寫beforeExecuteafterExecuteterminated方法來線上程執行任務前,執行緒執行任務結束,和執行緒終結前獲取執行緒的執行情況,根據具體情況調整執行緒池的執行緒數量

 使用場景

      1、當你的任務是非必要的時候。比如記錄操作日誌、通知第三方服務非必要資訊等,可以使用執行緒池處理非阻塞任務       2、當你的任務非常耗時時候,可以採用執行緒池技術       3、當請求併發很高時,可以採用執行緒池技術優化處理

    多執行緒是不是能加快處理速度?

         在使用多執行緒時,一定要知道一個道理:處理速度的最終決定因素是CPU、記憶體等,在單CPU(無論多少核)上,分配CPU資源的單位是“程序”而不是“執行緒”。

         我們可以做一個簡單的試驗:

         假設我要拷貝100萬條資料,單CPU電腦,用一個程序,在單執行緒的情況下,CPU佔用率為5%,耗時1000秒。那麼當在這個程序下,開闢10個執行緒同時去執行,是不是CPU佔用率增加到50%,耗時減少到100秒呢?顯然不是。我實測出來的情況是這樣的:

        “CPU佔用率仍然是5%,總耗時仍然是1000秒。且每個執行緒的執行時間也為1000秒。”

總結:

   第一,

      看硬體。如果是在比較強大的、多CPU的伺服器上執行程式,可以使用多執行緒來提高併發數和執行速度。

      但是執行緒也不宜過多,即使是16個CPU的伺服器,同一時間最多也只能真正意義上地併發處理16個執行緒,多出來的執行緒還是要等待。

第二,

     看用途。如果你不在乎處理速度,僅僅是為了提高併發處理能力,那麼理所當然地用多執行緒,

     但是如果你僅僅是想提高處理速度,且又是在單CPU機器上執行,那麼多執行緒並不值得。

      如果你的任務很耗時,且可以一部分、一部分地做,那麼最好不要用多執行緒(好比搬       磚,單執行緒一次搬10塊,總共搬10天,但搬一塊算一塊,到第9天的時候,你就搬完90塊磚了;

      如果你用10個執行緒同時去搬磚,同樣要搬10天,但是到第9天的時候,這10個執行緒100塊磚都“還在路上”,一塊磚都沒搬完!)。