1. 程式人生 > >【Java 並發】Executor框架機制與線程池配置使用

【Java 並發】Executor框架機制與線程池配置使用

需求 executor star 線程池 now() 等待 依賴 imu rup

【Java 並發】Executor框架機制與線程池配置使用

一,Executor框架Executor框架便是Java 5中引入的,其內部使用了線程池機制,在java.util.cocurrent 包下,通過該框架來控制線程的啟動、執行和關閉,可以簡化並發編程的操作。因此,在Java 5之後,通過Executor來啟動線程比使用Thread的start方法更好,更易管理,效率更好(用線程池實現,節約開銷)。

Executor框架主要包括:Executor,Executors,ExecutorService,AbstractExecutorService,ThreadPoolExecutor,CompletionService,Future,Callable等。

Executor

public interface Executor {
    void execute(Runnable command);
}

Executor是一個簡單的接口,但卻是整個執行框架的基礎,將任務的提交和執行分解開,並用Runnable 來表示任務,所以Executor也可看成基於生產者-消費者模式,提交任務的操作相當於生產者,執行任務的線程相當於消費者。Executor的實現還提供了對生命周期的支持,以及統計信息收集,應用程序管理機制和性能監視等機制。

ExecutorService

public interface ExecutorService extends Executor {
void shutdown();
List<Runnable> shutdownNow();
boolean isShutdown();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit)throws InterruptedException;
//........其他方法

ExecutorService擴展Executor,是一個比Executor使用更廣泛的子類接口,其提供了生命周期管理的方法,以及可跟蹤一個或多個異步任務執行狀況返回Future的方法。它主要包括三種狀態:運行、關閉、終止。創建後便進入運行狀態,當調用了shutdown()方法時,便進入平緩關閉狀態,ExecutorService不再接受新的任務,但它還在等待已經提交的任務完成,shutdownNow()就簡單粗暴,直接嘗試關閉所有任務,並不在接收任務。

AbstractExecutorServiceExecutorService執行方法的默認實現

ThreadPoolExecutor線程池,可以通過調用Executors以下靜態工廠方法來創建線程池並返回一個ExecutorService對象。

Executors

提供了一系列靜態工廠方法用於創建各種線程池:

1,newFixedThreadPool(int nThreads):創建一個線程池,該線程池重新使用一個固定數量的線程,該線程在共享的無界隊列中運行。在任何時候,大多數線程都將是活動的處理任務。如果在所有線程都處於活動狀態時提交額外的任務,它們將在隊列中等待,直到有一個線程可用。如果任何線程由於在關閉前的執行過程中出現故障而終止,那麽在需要執行後續任務時,將會有一個新的線程被執行。池中的線程將存在,直到ExecutorService顯式地執行服務關閉。

2,newCachedThreadPool():創建一個線程池,根據需要創建新的線程,但在可用時將重用以前構造的線程。這些池通常會提高執行許多短期異步任務的程序的性能。如果可用,對execute的調用將重用以前構造的線程。如果沒有可用的現有線程,將創建一個新線程並將其添加到池中。在60秒內沒有使用的線程被終止並從緩存中刪除。因此,一個閑置時間足夠長的池將不會消耗任何資源。具體超時時間可設置。

3,newSingleThreadExecutor() :創建一個執行器,該執行程序使用單個工作線程來操作一個無界隊列。(請註意,如果此單線程由於在關閉前執行失敗而終止,則在需要執行後續任務時將會出現一個新的線程。)任務被保證按順序執行,並且在任何給定的時間內都不會有一個任務是活動的。與其他等效的newFixedThreadPool(1)不同,返回的執行程序保證不會重新配置,以使用其他線程。

4, newScheduledThreadPool(int corePoolSize):創建一個線程池,該線程池可以調度命令在給定的延遲之後運行,或者定期執行。類似於Timer。

一般來說,CachedTheadPool在程序執行過程中通常會創建與所需數量相同的線程,然後在它回收舊線程時停止創建新線程,因此它是合理的Executor的首選,只有當這種方式會引發問題時(比如需要大量長時間面向連接的線程時),才需要考慮用FixedThreadPool。——《Thinking in Java》第四版

二,線程池配置使用

雖然Executor框架可以高效的管理線程,但是並非所有任務都適合Executor所有執行策略,這也會降低性能和效率。

1,線程饑餓死鎖

只要線程池中的任務需要無限期的等待一些必須由池中其他任務才能提供的資源或者條件,但是該任務卻還在工作隊列中還未有線程去執行,這就會造成線程饑餓死鎖。舉個栗子,假設只有單線程Executor,此時A任務正在運行,A任務等待B任務的結果才可繼續,於是就等待,但是此時是單線程,B任務一直無法運行,它在等A任務結束。於是兩個任務互相等待,造成死鎖。在更大的線程池中也能出現,除非線程池足夠大。

提交這種有依賴性的Executor,就需要避免饑餓死鎖,因此需要考慮線程池大小限制或者配置限制。

2,運行長時間任務

執行長時間的任務不僅會造成線程池阻塞,甚至還會增加執行時間短任務的服務時間,降低了Executor管理的服務的響應性。

緩解長時間任務造成的影響,可以限定任務等待資源的時間。如果等待超時,那麽標記任務失敗,然後中止任務或者稍後再執行任務。這種辦法保證任務總能繼續執行下去。如果線程池總是阻塞,那麽就要考慮線程池是否設置過小。

ThreadPoolExecutor提供了構造方法,可以根據需求配置線程池,進行定制

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) //後兩個參數為可選參數

具體的ThreadPoolExecutor參數配置

corePoolSize

核心線程數,核心線程會一直存活,即使沒有任務需要處理。當線程數小於核心線程數時,即使現有的線程空閑,線程池也會優先創建新線程來處理任務,而不是直接交給現有的線程處理。核心線程在allowCoreThreadTimeout被設置為true時會超時退出,默認情況下不會退出。

maximumPoolSize

當線程數大於或等於核心線程,且任務隊列已滿時,線程池會創建新的線程,直到線程數量達到maxPoolSize。如果線程數已等於maxPoolSize,且任務隊列已滿,則已超出線程池的處理能力,線程池會拒絕處理任務而拋出異常。

keepAliveTime:線程池維護線程所允許的空閑時間

unit:線程池維護線程所允許的空閑時間的單位

workQueue:線程池所使用的緩沖隊列

handler:線程池對拒絕任務的處理策略

當一個任務通過execute(Runnable)方法欲添加到線程池時:

1.當線程池小於corePoolSize時,新提交任務將創建一個新線程執行任務,即使此時線程池中存在空閑線程。

2.當線程池達到corePoolSize時,新提交任務將被放入workQueue中,等待線程池中任務調度執行 。

3.當workQueue已滿,且maximumPoolSize>corePoolSize時,新提交任務會創建新線程執行任務。

4.當提交任務數超過maximumPoolSize時,新提交任務由RejectedExecutionHandler處理 。

5.當線程池中超過corePoolSize線程,空閑時間達到keepAliveTime時,關閉空閑線程

6.當設置allowCoreThreadTimeOut(true)時,線程池中corePoolSize線程空閑時間達到keepAliveTime也將關閉。

java學習群669823128

【Java 並發】Executor框架機制與線程池配置使用