1. 程式人生 > >執行緒池的使用與分析(ThreadPoolExcutors)

執行緒池的使用與分析(ThreadPoolExcutors)

開發中為什麼使用執行緒池

1.降低資源的消耗:通過重複利用已經建立好的執行緒降低執行緒的建立和銷燬帶來的損耗

2.提高響應速度:因為執行緒池中的執行緒數沒有超過執行緒池的最大上限時,有的執行緒處於等待分配任務的狀態,當任務來時無需建立新的執行緒就能執行

3.提高執行緒的可管理性:執行緒池會根據當前系統特點對池內的執行緒進行優化處理,減少建立和銷燬執行緒帶來的系統開銷。無限的建立和銷燬執行緒不僅消耗系統資源,還降低系統的穩定性,使用執行緒池進行統一分配

執行緒池ThreadPoolExcutors

java提供了ThreadPoolExcutors來建立一個執行緒池,完整的建構函式如下:

public ThreadPoolExecutor(int 
corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)

1.int corePoolSize(核心執行緒數):執行緒池新建執行緒的時候,如果當前執行緒總數小於corePoolSize,則新建的是核心執行緒,如果超過corePoolSize,則新建的是非核心執行緒;核心執行緒預設情況下會一直存活線上程池中,即使這個核心執行緒啥也不幹(閒置狀態);如果設定了 allowCoreThreadTimeOut 為 true,那麼核心執行緒如果不幹活(閒置狀態)的話,超過一定時間(時長下面引數決定),就會被銷燬掉。

2.int maximumPoolSize(執行緒池能容納的最大執行緒數量):執行緒總數 = 核心執行緒數 + 非核心執行緒數。

3.long keepAliveTime(非核心執行緒空閒存活時長):非核心執行緒空閒時長超過該時長將會被回收,主要應用在快取執行緒池中,當設定了 allowCoreThreadTimeOut 為 true 時,對核心執行緒同樣起作用。

4.TimeUnit unit 空閒執行緒的存活時間(keepAliveTime 的單位)它是一個列舉型別,常用的如:TimeUnit.SECONDS(秒)、TimeUnit.MILLISECONDS(毫秒)。

5.BlockingQueue workQueue(任務佇列):當所有的核心執行緒都在幹活時,新新增的任務會被新增到這個佇列中等待處理,如果佇列滿了,則新建非核心執行緒執行任務

     常用的workQueue型別:

    (1)SynchornousQueue:這佇列接收到任務時會直接交給執行緒處理,而不保留它,如果所有的執行緒都在工作,那就建立一   個新的執行緒來處理這個任務,為了保證不出現執行緒數達到maxnumPoolSize而不能新建執行緒的錯誤,所以使用這個型別 的佇列時,maxnumPoolSize一般指定成Integer.MAX_VALUE,即無限大

    (2)LinkedBlockingQueue:這個佇列接收到任務的時候,如果當前執行緒數小於核心執行緒數,則新建執行緒(核心執行緒)處理任務;如果當前執行緒數等於核心執行緒數,則進入佇列等待。由於這個佇列沒有最大值限制,即所有超過核心執行緒數的任務都將被新增到佇列中,這也就導致了 maximumPoolSize 的設定失效,因為匯流排程數永遠不會超過 corePoolSize。

    (3)ArrayBlockingQueue:可以限定佇列的長度,接收到任務的時候,如果沒有達到 corePoolSize 的值,則新建執行緒(核心執行緒)執行任務,如果達到了,則入隊等候,如果佇列已滿,則新建執行緒(非核心執行緒)執行任務,又如果匯流排程數到了 maximumPoolSize,並且佇列也滿了,則發生錯誤。

    (4)DelayQueue:佇列內元素必須實現 Delayed 介面,這就意味著你傳進去的任務必須先實現 Delayed 介面。這個佇列接收到任務時,首先先入隊,只有達到了指定的延時時間,才會執行任務。

6.ThreadFactory threadFactory(執行緒工廠):用來建立執行緒池中的執行緒,通常用預設的即可

7.RejectedExecutionHandler handler(拒絕策略):線上程池已經關閉的情況下和任務太多導致最大執行緒數和任務佇列已經飽和,無法再接收新的任務,在上面兩種情況下,只要滿足其中一種時,在使用 execute() 來提交新的任務時將會拒絕,執行緒池提供了以下 4 種策略:

  1. AbortPolicy:預設策略,在拒絕任務時,會丟擲RejectedExecutionException。

  2. CallerRunsPolicy:只要執行緒池未關閉,該策略直接在呼叫者執行緒中,運行當前的被丟棄的任務。

  3. DiscardOldestPolicy:該策略將丟棄最老的一個請求,也就是即將被執行的任務,並嘗試再次提交當前任務。

  4. DiscardPolicy:該策略默默的丟棄無法處理的任務,不予任何處理。

四類常見的執行緒池

newCachedThreadPool建立一個可快取執行緒池,如果執行緒池長度超過處理需要,可靈活回收空閒執行緒,若無可回收,則新建執行緒。

newFixedThreadPool 建立一個定長執行緒池,可控制執行緒最大併發數,超出的執行緒會在佇列中等待。

newScheduledThreadPool 建立一個定長執行緒池,支援定時及週期性任務執行。

newSingleThreadExecutor 建立一個單執行緒化的執行緒池,它只會用唯一的工作執行緒來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先順序)執行。

其實他們都是通過ThreadPoolExcutors建立的

1.CachedThreadPool

由Executors的newCachedThreadPool方法建立,不存在核心執行緒,只存在數量不定的非核心執行緒,而且其數量最大值為Integer.MAX_VALUE。當執行緒池中的執行緒都處於活動時(全滿),執行緒池會建立新的執行緒來處理新的任務,否則就會利用新的執行緒來處理新的任務,執行緒池中的空閒執行緒都有超時機制,預設超時時長為60s,超過60s的空閒執行緒就會被回收。和FixedThreadPool不同的是,CachedThreadPool的任務佇列其實相當於一個空的集合,這將導致任何任務都會被執行,因為在這種場景下SynchronousQueue是不能插入任務的,SynchronousQueue是一個特殊的佇列,在很多情況下可以理解為一個無法儲存元素的佇列。從CachedThreadPool的特性看,這類執行緒比較適合執行大量耗時較小的任務。當整個執行緒池都處於閒置狀態時,執行緒池中的執行緒都會因為超時而被停止回收,幾乎是不佔任何系統資源。實現方式如下:

public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>(),
                                  threadFactory);
}
2.FixedThreadPool

由Executors的newFixedThreadPool方法建立。它是一種執行緒數量固定的執行緒池,當執行緒處於空閒狀態時,他們並不會被回收,除非執行緒池被關閉。當所有的執行緒都處於活動狀態時,新的任務都會處於等待狀態,直到有執行緒空閒出來。FixedThreadPool只有核心執行緒,且該核心執行緒都不會被回收,這意味著它可以更快地響應外界的請求。jdk實現如下:FixedThreadPool沒有額外執行緒,只存在核心執行緒,而且核心執行緒沒有超時機制,而且任務佇列沒有長度的限制。

public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>(),
                                  threadFactory);
}
3.ScheduledThreadPool

通過Executors的newScheduledThreadPool方式建立,核心執行緒數量是固定的,而非核心執行緒是沒有限制的,並且當非核心執行緒閒置時它會被立即回收,ScheduledThreadPool這類執行緒池主要用於執行定時任務和具有固定時期的重複任務,實現方法如下:

public static ScheduledExecutorService newScheduledThreadPool(
        int corePoolSize, ThreadFactory threadFactory) {
    return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
public ScheduledThreadPoolExecutor(int corePoolSize,
                                   ThreadFactory threadFactory) {
    super(corePoolSize, Integer.MAX_VALUE,
          DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
          new DelayedWorkQueue(), threadFactory);
}
4.SingleThreadPool

通過Executors的newSingleThreadExecutor方法來建立。這類執行緒池內部只有一個核心執行緒,它確保所有的任務都在同一個執行緒中按順序執行。SingleThreadExecutor的意義在於統一所有外界任務一個執行緒中,這使得這些任務之間不需要處理執行緒同步的問題,實現方式如下:

public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>(),
                                threadFactory));
}
引數表
執行緒池名稱corePoolSizemaxnumPoolSizekeepAliveTimeunitworkQueuethreadFactoryhandle使用場景
CachedThreadPool        0
Integer.MAX_VALUE
60SECONDSSynchronousQueuedefaultThreadFactorydefaultHandler處理執行時間比較短的任務
FixedThreadPool   nThread00MILLISECONDSLinkedBlockingQueuedefaultThreadFactorydefaultHandler已知併發壓力的情況下,對執行緒數做限制
ScheduleThreadPoolcorePoolSizeInteger.MAX_VALUE10MILLISECONDSDelayedWorkQueuedefaultThreadFactorydefaultHandler需要多個後臺執行緒執行週期任務的場景


SingleThreadPool110MILLISECONDSLinkedBlockingQueuedefaultThreadFactorydefaultHandler
需要保證順序執行的場景,並且只有一個執行緒在執行