任務的提交與異步執行


異步編程是一種對 CPU 資源更高效的編程方式,也是提高系統吞吐量的一個非常不錯的選擇。很多人會認為所謂的異步不就是多線程嗎?

但實際上這句話只能說對一半,沒錯,異步是通過多線程來實現的,但我們 Java 中的異步編程卻絕不僅僅只是多線程,它還包括對任務執行狀態的監控、隨時可以選擇性的中斷任務的執行以及獲取任務執行的返回結果。

Java 的並發包下為我們提供了一整套完善的異步任務框架,包括任務的定義、任務的提交、線程的創建與任務分配、監控任務狀態、取消任務等等,絕不僅僅局限於多線程的簡單創建與啟動。

簡單介紹與使用

下面我們先簡單介紹異步框架中的相關接口所代表的作用與含義,接着我簡單的編寫一個 demo 應用下我們異步框架。

1、任務的抽象

我們使用接口 Runnable 與 Callable 抽象的描述一個任務,前者相信大家已經非常的熟悉了,后者我們見的不多,但其實也是一個很簡單的接口,與 Runable 接口一樣也是一個函數式接口,內部定義一個 call 方法。

相比於 Runnable,除了內部定義的方法名稱不同外,call 方法還要求調用結束后返回一個結果,至於返回的結果是什么,取決於你的實現類,總的來說,兩者差別不大。

2、任務的執行

Executor 接口抽象了任務的執行者,所有的任務都可以向這里進行提交,Executor 會負責創建線程並啟動線程,執行任務。

Executor 接口的定義也是非常簡單的,只有一個 execute 執行方法。

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

ExecutorService 接口繼承了接口 Executor 並新增了更多的任務執行必須的方法,例如:

  • void shutdown();
  • List shutdownNow();
  • boolean isShutdown();
  • Future submit(Callable task);
  • Future submit(Runnable task, T result);
  • Future<?> submit(Runnable task);
  • invokeAll,invokeAny等

這些方法我們等會會深入去分析它們,這里大家只要有個印象就好,ExecutorService 允許我們將任務進行提交,它會統一地並在合適地時候創建線程、執行任務。

3、任務的監控

Future 接口用於監控我們的任務執行狀態,是已提交但未執行,或是已取消,亦或是已完成。Future 接口中定義的方法我們也不妨列舉部分感受一下:

  • boolean cancel(boolean mayInterruptIfRunning);
  • boolean isCancelled();
  • boolean isDone();
  • V get()

細心的同學可能已經發現了,任務的監控 Future 將在任務的提交成功后返回,也就是當你成功的調用 submit 方法之后,ExecutorService 將為你返回一個 Future 接口實例供你監控剛剛提交的任務執行狀態。

下面我們看一個簡單的 demo,用於演示基本的任務提交與執行。

demo

我們通過 Executors 的工廠方法獲取一個單線程的任務執行者,接着我們可以向這個任務執行者提交任務,當然這里簡化了代碼,使用了 Lambda 表達式,我們分別提交了兩個任務,並從 submit 方法的返回得到了任務的監控者 Future 實例。

接着,我們也就可以通過 Future 來得知任務執行的狀態。

總的來說,異步任務給我們帶來的好處是什么呢?我覺得最重要的一點就是「便捷」。

我只需要將我的任務提交就好了,不再關心如何如何創建線程,啟動線程等等細節,我也不再像以前一樣,線程啟動后根本不知道有沒有執行,我手里有 Future,我可以隨時的監控任務的執行情況。

另外,異步任務框架還有一點非常的不錯,那就是性能,它可以依賴線程池,減少線程創建和銷毀的開銷,這一切都將隨着 jdk 的迭代而不斷的優化,而我們在使用上根本不用關心,我只關心我的任務該怎么寫,至於任務怎么執行,如何高效低能耗,交給你異步框架了。

基本的實現原理

ExecutorService 繼承了 Executor 並新增了一些接口方法,這些方法數量還不少,而有些方法是很通用的,亦或是有些方法子類用不到,這你不能要求每一個子類實現者都實現了這些方法。

所以,向下又有了一層抽象,AbstractExecutorService 實現了 ExecutorService 並完成了很多方法的默認實現。后者只需要繼承 AbstractExecutorService 並重寫自己需要重寫的方法即可成為一個「異步任務的執行者」。

但是如下的方法 AbstractExecutorService 是沒有做默認實現的,需要你子類自行實現。原因也很簡單,因為這些方法不具備通用的邏輯,涉及到具體實現者內部使用的資源釋放,鎖資源競爭以及隊列資源的使用等,所以不太適合做抽象。

public void shutdown()
public List<Runnable> shutdownNow()
public boolean isShutdown()
public boolean isTerminated()
public boolean awaitTermination(long timeout, TimeUnit unit)
public void execute(Runnable command)

那我們就簡單點吧,直接從任務的提交開始看。

submit 主要有三種重載:

Future<?> submit(Runnable task)

Future submit(Runnable task, T result)

Future submit(Callable task)

因為任務的抽象表示主要有兩種,一種是 Runnable,一種是 Callable,所以需要提供對兩種不同任務類型的抽象提交。我們以其中一個重載進行分析即可,這里我們采用第一個重載方法:

public Future<?> submit(Runnable task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<Void> ftask = newTaskFor(task, null);
    execute(ftask);
    return ftask;
}

RunnableFuture 繼承了 Runnable 和 Future,標識這是一個可執行的、可監控的任務。而 newTaskFor 返回的是 FutureTask (RunnableFuture的一種實現類)。

而我們也不妨看看這個 FutureTask 內部都有些哪些成員:

任務執行狀態

state 和它可取的這些值共同描述了當前任務的執行狀態,是剛開始執行,還是正在執行中,還是正常結束,還是異常結束,還是被取消了,都由這個 state 來體現。

image

callable 代表當前正在執行的工作內容,這里說一下為什么只有 Callable 類型的任務,因為所有的 Runnable 類型任務都會被事先轉換成 Callable 類型,我覺得主要是統一和抽象實現吧。

outcome 是任務執行結束的返回值,runner 是正在執行當前任務的線程,waiters 是一個簡單的單鏈表,維護的是所有在任務執行結束之前嘗試調用 get 方法獲取執行結果的線程集合。當任務執行結束自當喚醒隊列中所有的線程。

除此之外,還有一個稍顯重要的方法,就是 run 方法,這個方法會在任務開始時由 ExecutorService 調用,這是一個很核心的方法,雖然方法體有點長,但是邏輯簡單,我們大體上概括下。

image

  1. 如果任務已經開始將退出方法邏輯的執行
  2. 調度任務執行,調用 call 方法
  3. 調用成功將保存結果,異常則將保存異常信息
  4. 處理中斷

其他的方法就不去看了,也比較多,還算是簡單的,如果有所想法,也歡迎你和我探討交流。

當我們回到 submit 方法時,其實就只剩下一個 execute 方法了,execute 方法是有點復雜的,也稍繁瑣,其中也涉及了一些線程池的概念,我們在下一篇分析線程池的時候再作詳盡分析了。

這里你只要知道,execute 會根據線程池中可用線程的數量,分配並選擇一個線程執行我們的任務即可。其他的一些細節我們后續再作討論。

關於異步任務我們這里作了簡單的介紹了,總體上你應該對 Java 的異步編程體系有一個認知了,細節之處並沒有很多,因為大多會涉及一些線程池的概念,我們還未介紹。

所以,后續也會結合線程池以及 Java8 新增的組合異步再作分析。

關注公眾不迷路,一個愛分享的程序員。

公眾號回復「1024」加作者微信一起探討學習!

每篇文章用到的所有案例代碼素材都會上傳我個人 github

https://github.com/SingleYam/overview_java

歡迎來踩!

YangAM 公眾號


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM