java中怎么理解Callable接口

本篇內(nèi)容介紹了“java中怎么理解Callable接口”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

企業(yè)建站必須是能夠以充分展現(xiàn)企業(yè)形象為主要目的,是企業(yè)文化與產(chǎn)品對(duì)外擴(kuò)展宣傳的重要窗口,一個(gè)合格的網(wǎng)站不僅僅能為公司帶來巨大的互聯(lián)網(wǎng)上的收集和信息發(fā)布平臺(tái),創(chuàng)新互聯(lián)公司面向各種領(lǐng)域:成都木包裝箱成都網(wǎng)站設(shè)計(jì)成都全網(wǎng)營銷推廣解決方案、網(wǎng)站設(shè)計(jì)等建站排名服務(wù)。


Callable、Executor 與 Future

既然是一個(gè)任務(wù)被執(zhí)行并返回結(jié)果,那么我們先來看看具體的任務(wù),也就是 Callable 接口。

任務(wù):Callable

非常簡單,只包含一個(gè)有泛型「返回值」的 call() 方法,需要在最后返回定義類型的結(jié)果。如果任務(wù)沒有需要返回的結(jié)果,那么將泛型 V 設(shè)為 void  并return null;就可以了。對(duì)比的是 Runnable,另一個(gè)明顯的區(qū)別則是 Callable可以拋出異常。

public interface Callable<V> {     V call() throws Exception; }   public interface Runnable {     public abstract void run(); }

執(zhí)行:ExecutorService

說到線程就少不了線程池,而說到線程池肯定離不開 Executor 接口。下面這幅圖是 Executor 的框架,我們常用的是其中的兩個(gè)具體實(shí)現(xiàn)類  ThreadPoolExecutor 以及 ScheduledThreadPoolExecutor,在 Executors  類中通過靜態(tài)方法獲取。Executors 中包含了線程池以及線程工廠的構(gòu)造,與 Executor 接口的關(guān)系類似于 Collection 接口和  Collections 類的關(guān)系。

java中怎么理解Callable接口

那么我們自頂向下,從源碼上了解一下 Executor 框架,學(xué)習(xí)學(xué)習(xí)任務(wù)是如何被執(zhí)行的。首先是 Executor 接口,其中只定義了 execute()  方法。

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

ExecutorService 接口繼承了 Executor 接口,主要擴(kuò)展了一系列的 submit() 方法以及對(duì) executor  的終止和判斷狀態(tài)。以第一個(gè) Future submit(Callable task);為例,其中 task 為用戶定義的執(zhí)行的異步任務(wù),F(xiàn)uture  表示了任務(wù)的執(zhí)行結(jié)果,泛型 T 代表任務(wù)結(jié)果的類型。

public interface ExecutorService extends Executor {      void shutdown();                // 現(xiàn)有任務(wù)完成后停止線程池       List<Runnable> shutdownNow();   // 立即停止線程池      boolean isShutdown();           // 判斷是否已停止      boolean isTerminated();      <T> Future<T> submit(Callable<T> task);        // 提交Callale任務(wù)      <T> Future<T> submit(Runnable task, T result);      Future<?> submit(Runnable task);      // 針對(duì)Callable集合的invokeAll()等方法 }

抽象類AbstractExecutorService 是 ThreadPoolExecutor  的基類,在下面的代碼中,它實(shí)現(xiàn)了ExecutorService 接口中的 submit() 方法。注釋中是對(duì)應(yīng)的 newTaskFor()  方法的代碼,非常簡單,就是將傳入的Callable 或 Runnable 參數(shù)封裝成一個(gè) FutureTask 對(duì)象。

// 1.第一個(gè)重載方法,參數(shù)為Callable public <T> Future<T> submit(Callable<T> task) {   if (task == null) throw new NullPointerException();   RunnableFuture<T> ftask = newTaskFor(task);   // return new FutureTask<T>(callable);   execute(ftask);   return ftask; }  // 2.第二個(gè)重載方法,參數(shù)為Runnable public Future<?> submit(Runnable task) {   if (task == null) throw new NullPointerException();   RunnableFuture<Void> ftask = newTaskFor(task, null);   // return new FutureTask<T>(task, null);   execute(ftask);   return ftask; }  // 3.第三個(gè)重載方法,參數(shù)為Runnable + 返回對(duì)象 public <T> Future<T> submit(Runnable task, T result) {   if (task == null) throw new NullPointerException();   RunnableFuture<T> ftask = newTaskFor(task, result);   // return new FutureTask<T>(task, result);   execute(ftask);   return ftask; }

那么也就是說,無論傳入的是 Callable 還是 Runnable,submit() 方法其實(shí)就做了三件事

java中怎么理解Callable接口

具體來說,submit() 中首先生成了一個(gè) RunnableFuture 引用的 FutureTask 實(shí)例,然后調(diào)用 execute()  方法來執(zhí)行它,那么我們可以推測 FutureTask 繼承自 RunnableFuture,而 RunnableFuture 又實(shí)現(xiàn)了  Runnable,因?yàn)閑xecute() 的參數(shù)應(yīng)為 Runnable 類型。上面還涉及到了 FutureTask 的構(gòu)造函數(shù),也來看一下。

public FutureTask(Callable<V> callable) {   this.callable = callable;   this.state = NEW; }  public FutureTask(Runnable runnable, V result) {   this.callable = Executors.callable(runnable, result); // 通過適配器將runnable在call()中執(zhí)行并返回result   this.state = NEW; }

FutureTask 共有兩個(gè)構(gòu)造方法。第一個(gè)構(gòu)造方法比較簡單,對(duì)應(yīng)上面的第一個(gè) submit(),采用組合的方式封裝Callable  并將狀態(tài)設(shè)為NEW;而第二個(gè)構(gòu)造方法對(duì)應(yīng)上面的后兩個(gè) submit() 重載,不同之處是首先使用了Executors.callable來將 Runnable 和  result 組合成 Callable,這里采用了適配器RunnableAdapter implements Callable,巧妙地在 call() 中執(zhí)行  Runnable 并返回結(jié)果。

static final class RunnableAdapter<T> implements Callable<T> {   final Runnable task;   final T result;                // 返回的結(jié)果;顯然:需要在run()中賦值    RunnableAdapter(Runnable task, T result) {     this.task = task;     this.result = result;   }   public T call() {     task.run();     return result;   } }

在適配器設(shè)計(jì)模式中,通常包含目標(biāo)接口 Target、適配器 Adapter 和被適配者 Adaptee  三類角色,其中目標(biāo)接口代表客戶端(當(dāng)前業(yè)務(wù)系統(tǒng))所需要的功能,通常為借口或抽象類;被適配者為現(xiàn)存的不能滿足使用需求的類;適配器是一個(gè)轉(zhuǎn)換器,也稱  wrapper,用于給被適配者添加目標(biāo)功能,使得客戶端可以按照目標(biāo)接口的格式正確訪問。對(duì)于 RunnableAdapter 來說,Callable  是其目標(biāo)接口,而 Runnable 則是被適配者。RunnableAdapter 通過覆蓋 call() 方法使其可按照 Callable  的要求來使用,同時(shí)其構(gòu)造方法中接收被適配者和目標(biāo)對(duì)象,滿足了 call() 方法有返回值的要求。

java中怎么理解Callable接口

那么總結(jié)一下 submit() 方法執(zhí)行的流程,就是:「Callable 被封裝在 Runnable 的子類中傳入 execute()  得以執(zhí)行」。

結(jié)果:Future

要說 Future 就是異步任務(wù)的執(zhí)行結(jié)果其實(shí)并不準(zhǔn)確,因?yàn)樗砹艘粋€(gè)任務(wù)的執(zhí)行過程,有狀態(tài)、可以被取消,而 get()  方法的返回值才是任務(wù)的結(jié)果。

public interface Future<V> {      boolean cancel(boolean mayInterruptIfRunning);      boolean isCancelled();      boolean isDone();      V get() throws InterruptedException, ExecutionException;      V get(long timeout, TimeUnit unit)         throws InterruptedException, ExecutionException, TimeoutException; }

我們?cè)谏厦嬷羞€提到了 RuunableFuture 和 FutureTask。從官方的注釋來看,RuunableFuture 就是一個(gè)可以 run的  future,實(shí)現(xiàn)了 Runnable 和 Future 兩個(gè)接口,在 run() 方法中執(zhí)行完計(jì)算時(shí)應(yīng)該將結(jié)果保存起來以便通過 get()獲取。

public interface RunnableFuture<V> extends Runnable, Future<V> {     /**      * Sets this Future to the result of its computation unless it has been cancelled.      */     void run(); }

FutureTask 直接實(shí)現(xiàn)了 RunnableFuture 接口,作為執(zhí)行過程,共有下面這幾種狀態(tài),其中 COMPLETING  為一個(gè)暫時(shí)狀態(tài),表示正在設(shè)置結(jié)果或異常,對(duì)應(yīng)的,設(shè)置完成后狀態(tài)變?yōu)?NORMAL 或 EXCEPTIONAL;CANCELLED、INTERRUPTED  表示任務(wù)被取消或中斷。在上面的構(gòu)造方法中,將 state 初始化為 NEW。

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;

然后是 FutureTask 的主要內(nèi)容,主要是 run() 和 get()。注意 outcome 的注釋,無論是否發(fā)生異常返回的都是這個(gè)  outcome,因?yàn)樵趫?zhí)行中如果執(zhí)行成功就將結(jié)果設(shè)置給了它(set()),而發(fā)生異常時(shí)將異常賦給了他(setException()),而在獲取結(jié)果時(shí)也都返回了  outcome(通過report())。

public class FutureTask<V> implements RunnableFuture<V> {          private Callable<V> callable;         // target,待執(zhí)行的任務(wù)          /** 保存執(zhí)行結(jié)果或異常,在get()方法中返回/拋出 */     private Object outcome; // 非volatile,通過CAS保證線程安全               public void run() {         ......         Callable<V> c = callable;         if (c != null && state == NEW) {             V result;             boolean ran;             try {                 result = c.call();            // 調(diào)用call()執(zhí)行用戶任務(wù)并獲取結(jié)果                 ran = true;                   // 執(zhí)行完成,ran置為true             } catch (Throwable ex) {          // 調(diào)用call()出現(xiàn)異常,而run()方法繼續(xù)執(zhí)行                  result = null;                  ran = false;                  setException(ex);                              // setException(Throwable t): compareAndSwapInt(NEW, COMPLETING);  outcome = t;                   }             if (ran)                 set(result);                                // set(V v): compareAndSwapInt(NEW, COMPLETING);  outcome = v;         }     }               public V get() throws InterruptedException, ExecutionException {         int s = state;         if (s <= COMPLETING)             s = awaitDone(false, 0L);         // 加入隊(duì)列等待COMPLETING完成,可響應(yīng)超時(shí)、中斷         return report(s);     }      public V get(long timeout, TimeUnit unit)         throws InterruptedException, ExecutionException, TimeoutException {         // 超時(shí)等待     }          private V report(int s) throws ExecutionException {         Object x = outcome;         if (s == NORMAL)                              // 將outcome作為執(zhí)行結(jié)果返回             return (V)x;         if (s >= CANCELLED)             throw new CancellationException();         throw new ExecutionException((Throwable)x);   // 將outcome作為捕獲的返回     } }

FutureTask 實(shí)現(xiàn)了 RunnableFuture 接口,所以有兩方面的作用。

第一,作為 Runnable 傳入 execute() 方法來執(zhí)行,同時(shí)封裝 Callable 對(duì)象并在 run() 中調(diào)用其 call()  方法;

第二,作為 Future 管理任務(wù)的執(zhí)行狀態(tài),將 call() 的返回值保存在 outcome 中以通過 get()  獲取。這似乎就能回答開頭的兩個(gè)問題,并且渾然天成,就好像是一個(gè)問題,除非發(fā)生異常的時(shí)候返回的不是任務(wù)的結(jié)果而是異常對(duì)象。

總結(jié)一下繼承關(guān)系:

java中怎么理解Callable接口

二、使用舉例

文章的標(biāo)題有點(diǎn)唬人,說到底還是講 Callable 的用法?,F(xiàn)在我們知道了 Future 代表了任務(wù)執(zhí)行的過程和結(jié)果,作為 call()  方法的返回值來獲取執(zhí)行結(jié)果;而 FutureTask 是一個(gè) Runnable 的 Future,既是任務(wù)執(zhí)行的過程和結(jié)果,又是 call  方法最終執(zhí)行的載體。下面通過一個(gè)例子看看他們?cè)谑褂蒙系膮^(qū)別。

首先創(chuàng)建一個(gè)任務(wù),即定義一個(gè)任務(wù)類實(shí)現(xiàn) Callable 接口,在 call() 方法里添加我們的操作,這里用耗時(shí)三秒然后返回 100  模擬計(jì)算過程。

class MyTask implements Callable<Integer> {     @Override     public Integer call() throws Exception {         System.out.println("子線程開始計(jì)算...");         for (int i=0;i<3;++i){             Thread.sleep(1000);             System.out.println("子線程計(jì)算中,用時(shí) "+(i+1)+" 秒");         }         System.out.println("子線程計(jì)算完成,返回:100");         return 100;     } }

然后呢,創(chuàng)建一個(gè)線程池,并實(shí)例化一個(gè) MyTask 備用。

ExecutorService executor = Executors.newCachedThreadPool(); MyTask task = new MyTask();

現(xiàn)在,分別使用 Future 和 FutureTask 來獲取執(zhí)行結(jié)果,看看他們有什么區(qū)別。

使用Future

Future 一般作為 submit() 的返回值使用,并在主線程中以阻塞的方式獲取異步任務(wù)的執(zhí)行結(jié)果。

System.out.println("主線程啟動(dòng)線程池"); Future<Integer> future = executor.submit(task); System.out.println("主線程得到返回結(jié)果:"+future.get()); executor.shutdown();

看看輸出結(jié)果:

主線程啟動(dòng)線程池 子線程開始計(jì)算... 子線程計(jì)算中,用時(shí) 1 秒 子線程計(jì)算中,用時(shí) 2 秒 子線程計(jì)算中,用時(shí) 3 秒 子線程計(jì)算完成,返回:100 主線程得到返回結(jié)果:100

主線程啟動(dòng)線程池子線程開始計(jì)算...子線程計(jì)算中,用時(shí) 1 秒子線程計(jì)算中,用時(shí) 2 秒子線程計(jì)算中,用時(shí) 3  秒子線程計(jì)算完成,返回:100主線程得到返回結(jié)果:100

由于 get() 方法阻塞獲取結(jié)果,所以輸出順序?yàn)樽泳€程計(jì)算完成后主線程輸出結(jié)果。

使用FutureTask

由于 FutureTask 集「任務(wù)與結(jié)果」于一身,所以我們可以使用 FutureTask 自身而非返回值來管理任務(wù),這需要首先利用 Callable  對(duì)象來構(gòu)造 FutureTask,并調(diào)用不同的submit()重載方法。

System.out.println("主線程啟動(dòng)線程池"); FutureTask<Integer> futureTask = new FutureTask<>(task); executor.submit(futureTask);                                 // 作為Ruunable傳入submit()中 System.out.println("主線程得到返回結(jié)果:"+futureTask.get());    // 作為Future獲取結(jié)果 executor.shutdown();

這段程序的輸出與上面中完全相同,其實(shí)兩者在實(shí)際執(zhí)行中的區(qū)別也不大,雖然前者調(diào)用了submit(Callable  task)而后者調(diào)用了submit(Runnable task),但最終都通過execute(futuretask)來把任務(wù)加入線程池中。

“java中怎么理解Callable接口”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

當(dāng)前標(biāo)題:java中怎么理解Callable接口
URL分享:http://muchs.cn/article8/ihiiip.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供服務(wù)器托管、網(wǎng)頁設(shè)計(jì)公司網(wǎng)站策劃、電子商務(wù)網(wǎng)站設(shè)計(jì)公司、網(wǎng)站排名

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

h5響應(yīng)式網(wǎng)站建設(shè)