Java線程池的配置

來源:互聯網
上載者:User

標籤:discard   adp   string   選項   handler   複雜   之間   log   sleep   

1、ThreadPoolExecutor的重要參數
 1、corePoolSize:核心線程數 * 核心線程會一直存活,及時沒有任務需要執行 * 當線程數小於核心線程數時,即使有線程空閑,線程池也會優先建立新線程處理 * 設定allowCoreThreadTimeout=true(預設false)時,核心線程會逾時關閉 2、queueCapacity:任務隊列容量(阻塞隊列) * 當核心線程數達到最大時,新任務會放在隊列中排隊等待執行 3、maxPoolSize:最大線程數 * 當線程數>=corePoolSize,且任務隊列已滿時。線程池會建立新線程來處理任務 * 當線程數=maxPoolSize,且任務隊列已滿時,線程池會拒絕處理任務而拋出異常 4、 keepAliveTime:線程空閑時間 * 當線程空閑時間達到keepAliveTime時,線程會退出,直到線程數量=corePoolSize * 如果allowCoreThreadTimeout=true,則會直到線程數量=0 5、allowCoreThreadTimeout:允許核心線程逾時 6、rejectedExecutionHandler:任務拒絕處理器 * 兩種情況會拒絕處理任務: - 當線程數已經達到maxPoolSize,切隊列已滿,會拒絕新任務 - 當線程池被調用shutdown()後,會等待線程池裡的任務執行完畢,再shutdown。如果在調用shutdown()和線程池真正shutdown之間提交任務,會拒絕新任務 * 線程池會調用rejectedExecutionHandler來處理這個任務。如果沒有設定預設是AbortPolicy,會拋出異常 * ThreadPoolExecutor類有幾個內部實作類別來處理這類情況: - AbortPolicy 丟棄任務,拋運行時異常 - CallerRunsPolicy 執行任務 - DiscardPolicy 忽視,什麼都不會發生 - DiscardOldestPolicy 從隊列中踢出最先進入隊列(最後一個執行)的任務 * 實現RejectedExecutionHandler介面,可自訂處理器
2、線程池隊列的選擇

wordQueue任務隊列,用於轉移和阻塞提交了的任務,即任務隊列是運行線程的,任務隊列根據corePoolSize和maximumPoolSize工作:

1.當正在啟動並執行線程小於corePoolSize,線程池會建立新的線程

2.當大於corePoolSize而任務隊列未滿時,就會將整個任務塞入隊列

3.當大於corePoolSize而且任務隊列滿時,並且小於maximumPoolSize時,就會建立新額線程執行任務

4、當大於maximumPoolSize時,會根據handler策略處理線程

任務隊列有以下三種模式:

1. 直接提交。工作隊列的預設選項是 SynchronousQueue,它將任務直接提交給線程而不保持它們。在此,如果不存在可用於立即運行任務的線程,則試圖把任務排入佇列將失敗,因此會構造一個新的線程。此策略可以避免在處理可能具有內部依賴性的請求集時出現鎖。直接提交通常要求無界 maximumPoolSizes 以避免拒絕新提交的任務。當命令以超過隊列所能處理的平均數連續到達時,此策略允許無界線程具有增長的可能性。  

2.無界隊列。使用無界隊列(例如,不具有預定義容量的 LinkedBlockingQueue)將導致在所有 corePoolSize 線程都忙時新任務在隊列中等待。這樣,建立的線程就不會超過 corePoolSize。(因此,maximumPoolSize 的值也就無效了。)當每個任務完全獨立於其他任務,即任務執行互不影響時,適合於使用無界隊列;例如,在 Web 頁伺服器中。這種排隊可用於處理瞬態突發請求,當命令以超過隊列所能處理的平均數連續到達時,此策略允許無界線程具有增長的可能性。

3.有界隊列。當使用有限的 maximumPoolSizes 時,有界隊列(如 ArrayBlockingQueue)有助於防止資源耗盡,但是可能較難調整和控制。隊列大小和最大池大小可能需要相互折衷:使用大型隊列和小型池可以最大限度地降低 CPU 使用率、作業系統資源和環境切換開銷,但是可能導致人工降低輸送量。如果任務頻繁阻塞(例如,如果它們是 I/O 邊界),則系統可能為超過您許可的更多線程安排時間。使用小型隊列通常要求較大的池大小,CPU 使用率較高,但是可能遇到不可接受的調度開銷,這樣也會降低輸送量。

3、線程池的飽和策略

  1.在預設的 ThreadPoolExecutor.AbortPolicy 中,處理常式遭到拒絕將拋出運行時 RejectedExecutionException。

   2.在 ThreadPoolExecutor.CallerRunsPolicy 中,線程調用運行該任務的 execute 本身。此策略提供簡單的反饋控制機制,能夠減緩新任務的提交速度。

   3.在 ThreadPoolExecutor.DiscardPolicy 中,不能執行的任務將被刪除。

   4.在 ThreadPoolExecutor.DiscardOldestPolicy 中,如果執行程式尚未關閉,則位於工作隊列頭部的任務將被刪除,然後重試執行程式(如果再次失敗,則重複此過程)。

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

public class Test {    public static void main(String[] args) throws InterruptedException {        //可以使用不同的abort和queue策略來看下執行的效果        RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardOldestPolicy  ();        ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 2,                200,                TimeUnit.MILLISECONDS,                new LinkedBlockingDeque<>(2),                handler);        for (int i = 0; i < 30; i++) {            MyTask myTask = new MyTask(i);            try {                executor.execute(myTask);            }catch (RejectedExecutionException e){                System.out.println("task "+i+" 被放棄");            }            System.out.println("線程池中線程數目:" + executor.getPoolSize() + ",隊列中等待執行的任務數目:" +                    executor.getQueue().size() + ",已執行完的任務數目:" + executor.getCompletedTaskCount());        }        //Thread.currentThread().sleep(400000);        executor.shutdown();    }}class MyTask implements Runnable {    private int taskNum;    public MyTask(int num) {        this.taskNum = num;    }    @Override    public void run() {        System.out.println("正在執行task " + taskNum);        try {            Thread.currentThread().sleep(1000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("task " + taskNum + "執行完畢");    }}

 

4、線程池的配置策略

通常情況下,這是一個複雜的活。

1.根據任務性質設定

要想合理的配置線程池,就必須首先分析任務特性,可以從以下幾個角度來進行分析:

1)任務的性質:CPU密集型任務,IO密集型任務和混合型任務。

2)任務的優先順序:高,中和低。

3)任務的執行時間:長,中和短。

4)任務的依賴性:是否依賴其他系統資源,如資料庫連接。

任務性質不同的任務可以用不同規模的線程池分開處理。CPU密集型任務配置儘可能小的線程,如配置CPU數+1個線程的線程池。IO密集型任務則由於線程並不是一直在執行任務,則配置儘可能多的線程,如2*CPU數。混合型的任務,如果可以拆分,則將其拆分成一個CPU密集型任務和一個IO密集型任務,只要這兩個任務執行的時間相差不是太大,那麼分解後執行的吞吐率要高於串列執行的吞吐率,如果這兩個任務執行時間相差太大,則沒必要進行分解。我們可以通過Runtime.getRuntime().availableProcessors()方法獲得當前裝置的CPU個數。

執行時間不同的任務可以交給不同規模的線程池來處理,或者也可以使用優先順序隊列,讓執行時間短的任務先執行。

依賴資料庫連接池的任務,因為線程提交SQL後需要等待資料庫返回結果,如果等待的時間越長CPU空閑時間就越長,那麼線程數應該設定越大,這樣才能更好的利用CPU。

建議使用有界隊列,有界隊列能增加系統的穩定性和預警能力,可以根據需要設大一點,比如幾千。我在測試一個線程池的時候,使用迴圈不斷提交新的任務,造成任務積壓線上程池,最後程式不斷的拋出拋棄任務的異常。如果使用無界隊列,線程池的隊列就會越來越多,有可能會撐滿記憶體,導致整個系統不可用,而不只是背景工作出現問題。

通常這種設定方式是比較粗略的方式。

2.利特爾法則

利特爾法則(Little’s law)是說,一個系統請求數等於請求的到達率與平均每個單獨請求花費的時間之乘積

我們可以使用利特爾法則(Little’s law)來判定線程池大小。我們只需計算請求到達率和請求處理的平均時間。然後,將上述值放到利特爾法則(Little’s law)就可以算出系統平均請求數。若請求數小於我們線程池的大小,就相應地減小線程池的大小。與之相反,如果請求數大於線程池大小,事情就有點複雜了。

當遇到有更多請求待處理的情況時,我們首先需要評估系統是否有足夠的能力支援更大的線程池。準確評估的前提是,我們必須評估哪些資源會限制應用程式的擴充能力。在本文中,我們將假定是CPU,而在實際中可能是其它資源。最簡單的情況是,我們有足夠的空間增加線程池的大小。若沒有的話,你不得不考慮其它選項,如軟體調優、增加硬體,或者調優並增加硬體。

具體的我們可以參考這篇文章:

http://www.infoq.com/cn/articles/Java-Thread-Pool-Performance-Tuning

3.設定檔中配置

如果是對系統效能非常重要的一個線程池,與其猜測該線程池的合理大小,不如將它的參數開放出來。因為線程池的合理大小和系統資源也是息息相關的,假設你在裝置A上面的線程池大小已經是最優了,不見得把程式放到裝置B上面同樣是最優的。放在設定檔中,可以方便將來根據系統運行情況進行調整。

我們看看開源任務調度架構Quartz開放了哪些參數:

<!-- 線程執行器配置,用於任務註冊  -->

<bean id="executor"class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">

    <property name="corePoolSize"value="6" />

    <property name="maxPoolSize"value="16" />

    <property name="queueCapacity"value="500" />

</bean>

Quart開放了核心線程數目、最大線程數目、任務隊列的容量這三個重要的參數,他們的設定都是和系統資源息息相關的。

4、線程池的監控

通過線程池提供的參數進行監控。線程池裡有一些屬性在監控線程池的時候可以使用:

taskCount:線程池需要執行的任務數量。

completedTaskCount:線程池在運行過程中已完成的任務數量。小於或等於taskCount。

largestPoolSize:線程池曾經建立過的最大線程數量。通過這個資料可以知道線程池是否滿過。如等於線程池的最大大小,則表示線程池曾經滿了。

getPoolSize:線程池的線程數量。如果線程池不銷毀的話,池裡的線程不會自動銷毀,所以這個大小隻增不+ getActiveCount:擷取活動的線程數。

通過擴充線程池進行監控。通過繼承線程池並重寫線程池的beforeExecute,afterExecute和terminated方法,我們可以在任務執行前,執行後和線程池關閉前幹一些事情。如監控任務的平均執行時間,最大執行時間和最小執行時間等。

Java線程池的配置

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.