線程池ThreadPoolTaskExecutor配置說明

     通常實際開發中常常用到多線程,因此須要使用線程池了,java

 ThreadPoolTaskExecutor一般經過XML方式配置,或者經過Executors的工廠方法進行配置。 
 XML方式配置代碼以下:交給spring來管理;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,用於被拒絕任務的處理程序,默認狀況下它將丟棄被拒絕的任務。優化

 


 

提交任務spa

    無返回值的任務使用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方法來在線程執行任務前,線程執行任務結束,和線程終結前獲取線程的運行狀況,根據具體狀況調整線程池的線程數量


 

 使用場景

 

      一、當你的任務是非必要的時候。好比記錄操做日誌、通知第三方服務非必要信息等,可使用線程池處理非阻塞任務 
      二、當你的任務很是耗時時候,能夠採用線程池技術 
      三、當請求併發很高時,能夠採用線程池技術優化處理

 


    多線程是否是能加快處理速度?

         在使用多線程時,必定要知道一個道理:處理速度的最終決定因素是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塊磚都「還在路上」,一塊磚都沒搬完!)。

相關文章
相關標籤/搜索