【Java編程的邏輯】異步任務執行服務 Executor和ExecutorService

基本概念

線程Thread即表示要執行的任務,又表示執行的機制。
Java併發包提供了一套“異步任務執行服務”機制,將“任務的提交”和“任務的執行”相分離。

任務執行服務涉及到的基本接口:

  • Runnable和Callable:表示要執行的異步任務
  • Executor和ExecutorService:表示執行服務
  • Future:表示異步任務的結果

Runnable和Callable都是接口,Runnable沒有返回結果,而Callable有,Runnable不會拋出異常,而Callable會。

Executor表示最簡單的執行服務

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

可以執行一個Runnable,沒有返回結果。接口沒有限定任務如何執行,可能是創建一個新線程,可能是複用線程池中的某個線程

ExecutorService擴展了Executor,定了更多服務
先來看submit方法,submit表示提交一個任務,返回值類型都是Future,返回後,只是表示任務已提交,不代表已執行,通過Future可以查詢異步任務的狀態、獲取最終結果、取消任務等。

public interface ExecutorService extends Executor { 
    // 對於Callable,任務最終有個返回值
    <T> Future<T> submit(Callable<T> task);
    // Runnable是沒有返回值的,但可以提供一個結果,在異步任務結束時返回
    <T> Future<T> submit(Runnable task, T result);
    // 最終返回null
    Future<?> submit(Runnable task);
} 

再來看看Future的定義:

public interface Future<V> { 
    // 用於取消異步任務,如果任務已完成、或已取消、或由於某種原因不能取消,返回false,否則返回true
    // 如果任務還未開始,則不再運行。如果任務已經在運行,則不一定能取消   
    // 參數mayInterruptIfRunning表示,如果任務正在執行,是否調用interrupt方法中斷線程,如果爲false就不會,如果爲true就會嘗試中斷線程,中斷不一定能取消線程  
    boolean cancel(boolean mayInterruptIfRunning);   
    // 表示任務是否被取消,只要cancel方法返回了true,隨後的isCancelled方法都會返回true,即使執行任務的線程還未真正結束  
    boolean isCancelled(); 
    // 表示任務是否結算,不管什麼原因導致結束的
    boolean isDone(); 
    // 返回執行結果,如果任務是Runnable且沒有提供結果,返回null
    // 任務被取消了,會拋出CancellationException 
    V get() throws InterruptedException, ExecutionException; 
    V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}

基本使用

public class Test {
    static class Task implements Callable<Integer> {
        @Override
        public Integer call() throws Exception {
            int sleepSeconds = new Random().nextInt(1000);
            Thread.sleep(sleepSeconds);
            return sleepSeconds;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        // 創建一個任務執行服務
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<Integer> future = executor.submit(new Task());
        Thread.sleep(100);
        try {
            System.out.println(future.get());
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        // 關閉任務執行服務
        executor.shutdown();
    }
}

ExecutorService還有如下幾個方法:

public interface ExecutorService extends Executor { 
    // 不再接受新任務,但已提交的任務會繼續執行,即使任務還未執行
    void shutdown();
    // 不接口新任務,且會終止已提交但未執行的任務,對於正在執行的任務,一般會調用線程的interrupt方法嘗試中斷
    // 返回已提交但尚未執行的任務列表  
    List<Runnable> shutdownNow();   

    // showdown和showdownNow不會阻塞,它們返回後不代表所有任務都已結束,但isShutdown會返回ture
    boolean isShutdown();

    // 除非首先調用shutdown或shutdownNow,否則isTerminated永不爲true。
    // 當調用shutdown()方法後,並且所有提交的任務完成後返回爲true;
    // 當調用shutdownNow()方法後,成功停止後返回爲true;
    boolean isTerminated(); 

    // 等待所有任務結束,可以限定等待的時間 
    // 如果超時前所有任務都結束了,則返回true,否則返回false
    boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;

    // 等待所有任務完成,返回Future列表 
    // 每個Future的isDone都返回true,不過isDone不代表任務執行成功了,可能是被取消了 
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) 
                    throws InterruptedException; 
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
                                  long timeout, TimeUnit unit) throws InterruptedException;
    // 只要有一個任務返回成功了,就會返回該任務的結果,其他任務會被取消 
    <T> T invokeAny(Collection<? extends Callable<T>> tasks)
        throws InterruptedException, ExecutionException; 
    <T> T invokeAny(Collection<? extends Callable<T>> tasks,
                    long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;     
}

實現原理

ExecutorService的主要實現類是ThreadPoolExecutor,它是基於線程實現的,繼承於AbstractExecutorService。AbstractExecutorService是一個抽象類,實現了ExecutorService的部分方法。

AbstractExecutorService

AbstractExecutorService提供了submit、invokeAll、invokeAny的默認實現,子類需要實現其他方法。
除了execute,其他方法都與執行服務的生命週期管理有關,submit/invokeAll/invokeAny最終都會調用execute,execute決定了到底如何執行任務。

我們可以先看看submit方法

public <T> Future<T> submit(Callable<T> task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<T> ftask = newTaskFor(task);
    execute(ftask);
    return ftask;
}

調用newTaskFor生成了一個RunnableFuture,RunnableFuture是一個接口,既擴展了Runnable,又擴展了Future,沒有定義新的方法。作爲Runnable它表示要執行的任務,傳遞給execute進行執行;作爲Future它表示任務執行的異步結果。

我們再看看newTaskFor

protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
    return new FutureTask<T>(callable);
}

FutureTask實現了RunnableFuture接口

FutureTask

public class FutureTask<V> implements RunnableFuture<V> {
    // 表示狀態  
    private volatile int state;
    // 剛開始的狀態或任務在運行
    private static final int NEW          = 0;
    // 臨時狀態,任務即將結果,在設置結果
    private static final int COMPLETING   = 1;
    // 任務正常執行完成
    private static final int NORMAL       = 2;
    // 任務執行拋出異常結束
    private static final int EXCEPTIONAL  = 3;
    // 任務被取消
    private static final int CANCELLED    = 4;
    // 任務在被中斷
    private static final int INTERRUPTING = 5;
    // 任務被中斷
    private static final int INTERRUPTED  = 6;

    // 表示待執行的任務
    private Callable<V> callable;

    // 表示最終的執行結果或異常
    private Object outcome;

    // 表示運行任務的線程
    private volatile Thread runner;

    // 單向鏈表,表示等待任務執行結果的線程
    private volatile WaitNode waiters;

    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        // 初始化狀態
        this.state = NEW;       // ensure visibility of callable
    }

    public FutureTask(Runnable runnable, V result) {
        // 如果傳入的是runnable,則轉換爲callable
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }
}

任務執行服務會使用一個線程執行FutureTask的run方法

public void run() {
    if (state!= NEW ||
        !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                     null, Thread.currentThread()))
        return;
    try {
        Callable<V> c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                // 調用callable的call方法
                result = c.call();
                ran = true;
            } catch (Throwable ex) {
                // 捕捉異常,異常保存到outcome並調用finishCompletion喚醒所有等待結果的線程
                result = null;
                ran = false;
                setException(ex);
            }
            // 設置結果,保存到outcome並調用finishCompletion喚醒所有等待結果的線程
            if (ran)
                set(result);
        }
    } finally {
        // runner must be non-null until state is settled to
        // prevent concurrent calls to run()
        runner = null;
        // state must be re-read after nulling runner to prevent
        // leaked interrupts
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}

任務提交者,通過get方法獲取結果,

public V get(long timeout, TimeUnit unit)
    throws InterruptedException, ExecutionException, TimeoutException {
    if (unit == null)
        throw new NullPointerException();
    int s = state;
    // 如果任務還未執行完畢就等待
    if (s <= COMPLETING &&
        (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
        throw new TimeoutException();
    // 調用report報告結果,report根據狀態返回結果或拋出異常
    return report(s);
}

private V report(int s) throws ExecutionException {
    Object x = outcome;
    if (s == NORMAL)
        return (V)x;
    if (s >= CANCELLED)
        throw new CancellationException();
    throw new ExecutionException((Throwable)x);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章