java併發程式設計——執行緒池

ahiru?發表於2019-02-12

為了避免重複的建立執行緒,執行緒池的出現可以讓執行緒進行復用。通俗來講,當有任務提交時,就會向執行緒池拿一個執行緒,當任務完成後,並不是直接關閉執行緒,而是將這個執行緒歸還給執行緒池供其他任務使用。

java併發程式設計——執行緒池

ThreadPoolExecutor:

ThreadPoolExecutor是執行緒池的實現類。

ThreadPoolExecutor的構造器:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.acc = System.getSecurityManager() == null ?
            null :
            AccessController.getContext();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}複製程式碼

構造器引數解析:

corePoolSize:執行緒池的核心池大小,在建立執行緒池之後,執行緒池預設沒有任何執行緒。除非呼叫了prestartAllCoreThreads()或prestartCoreThread()方法,這兩個方法是預建立執行緒的方法,即在沒有任務到來之前會建立corePoolSize個執行緒或者一個執行緒。預設情況下,在建立了執行緒池後,執行緒池中的執行緒數為0,當有任務提交,就會建立一個執行緒去執行任務,當執行緒池的執行緒數目達到corePoolSize後,就會把到達的任務放到快取佇列中。

maximumPoolSize:執行緒池的最大執行緒數。如果阻塞佇列滿了,並且已建立的執行緒數小於最大執行緒數,則執行緒池會在建立新的執行緒執行。

keepAliveTime:表示執行緒沒有任務執行時最多保持多久時間會終止。預設情況下,當執行緒池中的執行緒數大於corePoolSize時,如果一個執行緒空閒時間達到keepAliveTime,執行緒則會終止,直到執行緒池中的執行緒數不大於corePoolSize。如果呼叫了allowCoreThreadTimeOut(boolean)方法,線上程池中的執行緒數不大於corePoolSize時,keepAliveTime引數也會起作用,直到執行緒池中的執行緒數為0。

unit:引數keepAliveTime的時間單位。

workQueue:阻塞佇列,用來儲存等待執行的任務。阻塞佇列一般有以下幾種:

ArrayBlockingQueue;//陣列結構的有界阻塞佇列,先進先出,建立時必須指定大小
LinkedBlockingQueue;//連結串列結構的無界阻塞佇列,先進先出
SynchronousQueue;//不儲存元素的阻塞佇列,每次插入操作必須等到另一個執行緒呼叫移除操作複製程式碼

threadFactory:執行緒工廠,主要用來建立執行緒

handler:拒絕處理任務策略,有以下四種取值。

ThreadPoolExecutor.AbortFactory;//丟棄任務並丟擲RejectedExecutionException異常
ThreadPoolExecutor.DiscardPolicy;//丟棄任務,但不丟擲異常
ThreadPoolExecutor.DiscardOldestPolicy;//丟棄佇列最前面的任務,然後重新嘗試執行任務(重複此過程)
ThreadPoolExecutor.CallerRunsPolicy;//由呼叫執行緒處理該任務複製程式碼

執行緒池的任務處理策略

java併發程式設計——執行緒池

首先取得當前執行緒池中的執行緒總數,與核心池大小比較,

  • 如果小於核心池大小,將通過addWorker()方法建立執行緒執行
  • 如果大於,則提交到阻塞佇列
  • 如果阻塞佇列已滿,則把任務直接提交到執行緒池
  • 如果當前執行緒數達到最大執行緒數,則提交失敗,執行拒絕策略,否則分配執行緒執行。

執行緒池的關閉

ThreadPoolExecutor提供了兩個方法關閉執行緒池:

shutdown();//不會立即終止執行緒池,等所有的任務和阻塞佇列的任務都執行完才終止,但不再接受新的任務。
shutdownNow();//立即終止執行緒池,並嘗試打斷正在執行的任務,並清空阻塞佇列,返回尚未執行的任務複製程式碼

執行緒池的狀態

volatile int runState;
static final int RUNNING = 0;//執行狀態
static final int SHUTDOWN = 1;//關閉狀態
static final int STOP = 2;//停止
static final int TERMINATED = 3;//終止複製程式碼

  • 建立執行緒池後,初始狀態為RUNNING
  • 如果呼叫了shutdown(),則執行緒池處於SHUTDOWN狀態,此時執行緒池不再接受新的任務,它會等所有任務執行完畢,最後終止。
  • 如果呼叫了shutdownNow(),執行緒池處於STOP狀態,此時執行緒池不能接受新的任務,並且去嘗試終止正在執行的任務,返回沒有執行的任務列表。
  • 當執行緒池處於SHUTDOWN或STOP狀態,並且所以工作執行緒已經銷燬,阻塞佇列已經清空或執行完之後,執行緒池被設定為TERMINATED狀態

常見的四種執行緒池

FixedThreadPool:固定大小的執行緒池

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}複製程式碼

固定大小的執行緒池,該執行緒池corePoolSize和maximumPoolSize相等,阻塞佇列為LinkedBlockingQueue。固定大小的執行緒池,不存線上程數量的變化。

SingleThreadExecutor:單執行緒執行緒池

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}複製程式碼

單執行緒執行緒池,只有一個執行緒的執行緒池,阻塞佇列用LinkedBlockingQueue,若有多餘的任務提交到執行緒池中,則會被暫存到阻塞佇列,等空閒時再去執行,按照先進先出的順序執行。該執行緒池可保證任務按照被加入順序執行。

CachedThreadPool:快取執行緒池

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}複製程式碼

快取執行緒池,快取的執行緒預設存活60秒。執行緒池的corePoolSize大小為0,最大執行緒數為Integer.MAX_VALUE,阻塞佇列使用的是SynchronousQueue,是一個直接提交的阻塞佇列,它總會迫使執行緒池增加新的執行緒去執行任務。當執行緒的空間時間超過60s,則工作執行緒將會終止被回收。當提交新任務時,如果沒有空閒執行緒,則建立新執行緒執行任務。

ScheduledThreadPool:定時執行緒池

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}複製程式碼

定時執行緒池,可用於週期性地執行任務,通常用於週期性的同步資料。

參考資料:淺入深理解Java執行緒池及執行緒池的如何使用


相關文章