Java多線程系列--“JUC線程池”06之 Callable和Future


 

概要

本章介紹線程池中的Callable和Future。
Callable 和 Future 簡介
示例和源碼分析(基於JDK1.7.0_40)

轉載請注明出處:http://www.cnblogs.com/skywang12345/p/3544116.html

 

Callable 和 Future 簡介

  Callable 和 Future 是比較有趣的一對組合。當我們需要獲取線程的執行結果時,就需要用到它們。Callable用於產生結果,Future用於獲取結果。

1. Callable

Callable 是一個接口,它只包含一個call()方法。Callable是一個返回結果並且可能拋出異常的任務。

為了便於理解,我們可以將Callable比作一個Runnable接口,而Callable的call()方法則類似於Runnable的run()方法。

Callable的源碼如下:

public interface Callable<V> {
    V call() throws Exception;
}

說明:從中我們可以看出Callable支持泛型。

 

2. Future

Future 是一個接口。它用於表示異步計算的結果。提供了檢查計算是否完成的方法,以等待計算的完成,並獲取計算的結果。

Future的源碼如下:

public interface Future<V> {
    // 試圖取消對此任務的執行。
    boolean     cancel(boolean mayInterruptIfRunning)

    // 如果在任務正常完成前將其取消,則返回 true。
    boolean     isCancelled()

    // 如果任務已完成,則返回 true。
    boolean     isDone()

    // 如有必要,等待計算完成,然后獲取其結果。
    V           get() throws InterruptedException, ExecutionException;

    // 如有必要,最多等待為使計算完成所給定的時間之后,獲取其結果(如果結果可用)。
    V             get(long timeout, TimeUnit unit)
          throws InterruptedException, ExecutionException, TimeoutException;
}

說明: Future用於表示異步計算的結果。它的實現類是FutureTask,在講解FutureTask之前,我們先看看Callable, Future, FutureTask它們之間的關系圖,如下:

說明
(01) RunnableFuture是一個接口,它繼承了Runnable和Future這兩個接口。RunnableFuture的源碼如下:

public interface RunnableFuture<V> extends Runnable, Future<V> {
    void run();
}

(02) FutureTask實現了RunnableFuture接口。所以,我們也說它實現了Future接口。

 

示例和源碼分析(基於JDK1.7.0_40)

我們先通過一個示例看看Callable和Future的基本用法,然后再分析示例的實現原理。

 1 import java.util.concurrent.Callable;
 2 import java.util.concurrent.Future;
 3 import java.util.concurrent.Executors;
 4 import java.util.concurrent.ExecutorService;
 5 import java.util.concurrent.ExecutionException;
 6 
 7 class MyCallable implements Callable {
 8 
 9     @Override 
10     public Integer call() throws Exception {
11         int sum    = 0;
12         // 執行任務
13         for (int i=0; i<100; i++)
14             sum += i;
15         //return sum; 
16         return Integer.valueOf(sum);
17     } 
18 }
19 
20 public class CallableTest1 {
21 
22     public static void main(String[] args) 
23         throws ExecutionException, InterruptedException{
24         //創建一個線程池
25         ExecutorService pool = Executors.newSingleThreadExecutor();
26         //創建有返回值的任務
27         Callable c1 = new MyCallable();
28         //執行任務並獲取Future對象 
29         Future f1 = pool.submit(c1);
30         // 輸出結果
31         System.out.println(f1.get()); 
32         //關閉線程池 
33         pool.shutdown(); 
34     }
35 }

運行結果

4950

結果說明
  在主線程main中,通過newSingleThreadExecutor()新建一個線程池。接着創建Callable對象c1,然后再通過pool.submit(c1)將c1提交到線程池中進行處理,並且將返回的結果保存到Future對象f1中。然后,我們通過f1.get()獲取Callable中保存的結果;最后通過pool.shutdown()關閉線程池。

 

1. submit()

submit()在java/util/concurrent/AbstractExecutorService.java中實現,它的源碼如下:

public <T> Future<T> submit(Callable<T> task) {
    if (task == null) throw new NullPointerException();
    // 創建一個RunnableFuture對象
    RunnableFuture<T> ftask = newTaskFor(task);
    // 執行“任務ftask”
    execute(ftask);
    // 返回“ftask”
    return ftask;
}

說明:submit()通過newTaskFor(task)創建了RunnableFuture對象ftask。它的源碼如下:

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

 

2. FutureTask的構造函數

FutureTask的構造函數如下:

public FutureTask(Callable<V> callable) {
    if (callable == null)
        throw new NullPointerException();
    // callable是一個Callable對象
    this.callable = callable;
    // state記錄FutureTask的狀態
    this.state = NEW;       // ensure visibility of callable
}

 

3. FutureTask的run()方法

我們繼續回到submit()的源碼中。
在newTaskFor()新建一個ftask對象之后,會通過execute(ftask)執行該任務。此時ftask被當作一個Runnable對象進行執行,最終會調用到它的run()方法;ftask的run()方法在java/util/concurrent/FutureTask.java中實現,源碼如下:

public void run() {
    if (state != NEW ||
        !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                     null, Thread.currentThread()))
        return;
    try {
        // 將callable對象賦值給c。
        Callable<V> c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                // 執行Callable的call()方法,並保存結果到result中。
                result = c.call();
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                setException(ex);
            }
            // 如果運行成功,則將result保存
            if (ran)
                set(result);
        }
    } finally {
        runner = null;
        // 設置“state狀態標記”
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}

說明run()中會執行Callable對象的call()方法,並且最終將結果保存到result中,並通過set(result)將result保存。
      之后調用FutureTask的get()方法,返回的就是通過set(result)保存的值。

 


更多內容

1. Java多線程系列--“JUC線程池”02之 線程池原理(一)

2. Java多線程系列--“JUC線程池”03之 線程池原理(二)

3. Java多線程系列--“JUC線程池”04之 線程池原理(三)

4. Java多線程系列--“JUC線程池”05之 線程池原理(四)

5. Java多線程系列--“JUC線程池”01之 線程池架構

6. Java多線程系列目錄(共xx篇)

 


免責聲明!

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



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