1. 程式人生 > 實用技巧 >java執行緒池總結

java執行緒池總結

Executor 框架

2.1 簡介

Executor 框架是 Java5 之後引進的,在 Java 5 之後,通過 Executor 來啟動執行緒比使用 Thread 的 start 方法更好,除了更易管理,效率更好(用執行緒池實現,節約開銷)外,還有關鍵的一點:有助於避免 this 逃逸問題。

補充:this 逃逸是指在建構函式返回之前其他執行緒就持有該物件的引用. 呼叫尚未構造完全的物件的方法可能引發令人疑惑的錯誤。

Executor 框架不僅包括了執行緒池的管理,還提供了執行緒工廠、佇列以及拒絕策略等,Executor 框架讓併發程式設計變得更加簡單。

2.2 Executor 框架結構(主要由三大部分組成)

1) 任務(Runnable/Callable)

執行任務需要實現的Runnable介面或Callable介面。Runnable介面或Callable介面實現類都可以被ThreadPoolExecutorScheduledThreadPoolExecutor執行。

2) 任務的執行(Executor)

如下圖所示,包括任務執行機制的核心介面Executor,以及繼承自Executor介面的ExecutorService介面。ThreadPoolExecutorScheduledThreadPoolExecutor這兩個關鍵類實現了ExecutorService 介面。

這裡提了很多底層的類關係,但是,實際上我們需要更多關注的是ThreadPoolExecutor

這個類,這個類在我們實際使用執行緒池的過程中,使用頻率還是非常高的。

注意:通過檢視ScheduledThreadPoolExecutor原始碼我們發現ScheduledThreadPoolExecutor實際上是繼承了ThreadPoolExecutor並實現了 ScheduledExecutorService ,而ScheduledExecutorService又實現了ExecutorService,正如我們下面給出的類關係圖顯示的一樣。

ThreadPoolExecutor類描述:

//AbstractExecutorService實現了ExecutorService介面
public
class ThreadPoolExecutor extends AbstractExecutorService

ScheduledThreadPoolExecutor類描述:

//ScheduledExecutorService實現了ExecutorService介面
public class ScheduledThreadPoolExecutor
        extends ThreadPoolExecutor
        implements ScheduledExecutorService


3) 非同步計算的結果(Future)


Future介面以及Future介面的實現類FutureTask類都可以代表非同步計算的結果。


當我們把Runnable介面或Callable介面的實現類提交給ThreadPoolExecutorScheduledThreadPoolExecutor執行。(呼叫submit()方法時會返回一個FutureTask物件)


2.3 Executor 框架的使用示意圖



  1. 主執行緒首先要建立實現Runnable或者Callable介面的任務物件。
  2. 把建立完成的實現Runnable/Callable介面的 物件直接交給ExecutorService執行:ExecutorService.execute(Runnable command))或者也可以把Runnable物件或Callable物件提交給ExecutorService執行(ExecutorService.submit(Runnable task)ExecutorService.submit(Callable <T> task))。
  3. 如果執行ExecutorService.submit(…)ExecutorService將返回一個實現Future介面的物件(我們剛剛也提到過了執行execute()方法和submit()方法的區別,submit()會返回一個FutureTask 物件)。由於 FutureTask實現了Runnable,我們也可以建立FutureTask,然後直接交給ExecutorService執行。
  4. 最後,主執行緒可以執行FutureTask.get()方法來等待任務執行完成。主執行緒也可以執行FutureTask.cancel(boolean mayInterruptIfRunning)來取消此任務的執行。

(重要)ThreadPoolExecutor 類簡單介紹

執行緒池實現類ThreadPoolExecutorExecutor框架最核心的類。

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.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

下面這些對建立 非常重要,在後面使用執行緒池的過程中你一定會用到!所以,務必拿著小本本記清楚。

ThreadPoolExecutor3 個最重要的引數:

  • corePoolSize:核心執行緒數執行緒數定義了最小可以同時執行的執行緒數量。
  • maximumPoolSize:當佇列中存放的任務達到佇列容量的時候,當前可以同時執行的執行緒數量變為最大執行緒數。
  • workQueue:當新任務來的時候會先判斷當前執行的執行緒數量是否達到核心執行緒數,如果達到的話,信任就會被存放在佇列中。

ThreadPoolExecutor其他常見引數:

  1. keepAliveTime:當執行緒池中的執行緒數量大於corePoolSize的時候,如果這時沒有新的任務提交,核心執行緒外的執行緒不會立即銷燬,而是會等待,直到等待的時間超過了keepAliveTime才會被回收銷燬;
  2. unit:keepAliveTime引數的時間單位。
  3. threadFactory:executor 建立新執行緒的時候會用到。
  4. handler:飽和策略。關於飽和策略下面單獨介紹一下。

下面這張圖可以加深你對執行緒池中各個引數的相互關係的理解(圖片來源:《Java 效能調優實戰》):