Future與FutureTask都是用於獲取線程執行的返回結果。下面咱們就對二者之間的關係與使用進行一個大體的介紹與分析java
1、Future與FutureTask介紹:函數
Future位於java.util.concurrent包下,它是一個接口this
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; }
Future接口中聲明瞭5個方法,下面介紹一下每一個方法的做用:spa
cancel方法用來取消任務,取消成功則返回true,取消失敗則返回false。參數mayInterruptIfRunning設置爲false,表示不容許在線程運行時中斷,設置爲true則表示容許。具體可分爲如下三種狀況:線程
一、若是任務已經完成,則不管mayInterruptIfRunning爲true仍是false,都返回false,這是由於你要取消的任務已經完成,則認爲取消任務失敗;code
二、若是任務正在執行,則不管mayInterruptIfRunning爲true仍是false,都返回true。只不過mayInterruptIfRunning爲true時線程會被中斷,false時線程不會被中斷會執行完。對象
三、若是任務尚未執行,則不管mayInterruptIfRunning爲true仍是false,都返回true。blog
isCancelled方法用於判斷任務是否被取消成功,cancel方法成功則返回 true,反之則爲false。繼承
isDone用於判斷任務是否完成, 若是任務完成則返回true。任務完成包括正常結束、任務被取消、任務發生異常,都返回true接口
get()方法用來獲取執行結果,這個方法會產生阻塞,會一直等到任務執行完畢才返回;
get(long timeout, TimeUnit unit)用來獲取執行結果,若是在指定時間內,還沒獲取到結果,拋出 java.util.concurrent.TimeoutException 異常
FutureTask 實現了RunnableFuture接口,而RunnableFuture則繼承了Future<V>與Runnable接口,因此 FutureTask不只實現了 Future<V>接口的全部方法,還具備本身的run方法,咱們能夠看下它的類圖
二、Future與FutureTask使用與分析
一、使用Future時,咱們須要實現Callable接口,並經過ExecutorService接口的submit方法獲取返回的Future對象,
二、使用FutureTask時,根據FutureTask的構造函數能夠看到FutureTask既能夠接收Callable的實現類,也能夠接收Runnable的實現類。當你傳入的是Callable的實現類時,能夠獲取線程執行的結果;傳入Runnable的實現類時,因爲Runnable的實現沒有返回值,須要傳入一個你設置的線程完成標識,也就是result,而後當線程結束時會把你傳入的result原值返回給你,FutureTask的構造函數具體以下:
public class FutureTask<V> implements RunnableFuture<V>{ 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) { this.callable = Executors.callable(runnable, result);//runnable轉化爲callable this.state = NEW; // ensure visibility of callable } }
接下來咱們看下Future與FutureTask具體的使用代碼:
// 執行任務 實現Runnable FutureTaskJobRunnable taskRun = new FutureTaskJobRunnable(); // 執行任務 實現Callable FutureTaskJobCallable taskCall = new FutureTaskJobCallable(); String val = "ok"; // 線程運行成功後把,返回你傳入的val值 FutureTask<String> futureTaskRun = new FutureTask<String>(taskRun, val); // 線程運行,返回線程執行的結果 FutureTask<String> futureTaskCall = new FutureTask<String>(taskCall); //聲明線程池 ExecutorService executor = Executors.newCachedThreadPool(); //Future Future<String> future = executor.submit(taskCall); System.out.println(future.get()); //FutureTask executor.submit(futureTaskCall); System.out.println(futureTaskCall.get()); //FutureTask自定義線程執行 new Thread(futureTaskRun).start(); System.out.println(futureTaskRun.get());
public class FutureTaskJobCallable implements Callable<String>{ public String call() throws Exception { System.out.println("FutureTaskJobCallable已經執行了哦"); Thread.sleep(1000); return "返回結果"; } } public class FutureTaskJobRunnable implements Runnable { public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("FutureTaskJobRunnable已經執行了哦"); } }
根據上面的代碼咱們從ExecutorService接口中submit方法入手,看下AbstractExecutorService類對submit方法的具體實現。
public Future<?> submit(Runnable task) { if (task == null) throw new NullPointerException(); RunnableFuture<Void> ftask = newTaskFor(task, null); execute(ftask); return ftask; } public <T> Future<T> submit(Runnable task, T result) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task, result); execute(ftask); return ftask; } public <T> Future<T> submit(Callable<T> task) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task); execute(ftask); return ftask; } protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) { return new FutureTask<T>(runnable, value); } protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) { return new FutureTask<T>(callable); }
能夠看到當你使用submit方法提交任務時,都會經過newTaskFor方法轉換成FutureTask對象,因此咱們具體分析下上面代碼中的三種狀況:
一、若是你傳入的是本身實現的Runaable類或者Callable類,那麼sumbit方法天然會幫你自動封裝爲FutureTask對象,運行後經過Future對象獲取結果。
二、你傳入的已是個本身構造的FutureTask對象,因爲FutureTask實際上是實現了Runnable接口的,它自己就是個Runaable實現類, sumbit方法仍是會將它視爲Runnable類來進行封裝,並最終會執行FutureTask本身的run方法,一系列實現都在你傳入的FutureTask對象內完成,因此你能夠直接經過本身構建的FutureTask獲取結果;
三、本身單獨聲明線程運行,跟第2點相似,FutureTask自己就是個Runnabel實現類,天然能夠作爲參數傳入Thread運行;
那麼咱們把自定義的Runnable、Callable實現類作爲參數構造FutureTask後,FuttureTask是如何運行的呢,咱們能夠看下FuttureTask中具體的代碼實現
//你傳入的Runnable與Callable實現類都會在構造函數中轉化爲Callable private Callable<V> callable; 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 { result = c.call();//運行後返回結果 ran = true; } catch (Throwable ex) { result = null; ran = false; setException(ex); } 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); } }
能夠看到FutureTask類自己的run方法,就是執行Runnable、Callable的實現類並獲取返回結果的過程。
因此ExecutorService接口中submit方法歸根結底仍是要把你傳入的對象封裝成FutureTask對象,並經過FutureTask類的內部實現來獲取結果的,返回的Future接口對象也要依賴於FutureTask實例化的,因此不管是直接傳入本身的Runnable、Callable實現類仍是構建FutureTask傳入,本質上都是經過FutureTask去實現,沒有什麼區別;
以上就是對Future與FutureTask二者關係的一個大體的介紹與分析,其中若有不足與不正確的地方還望指出與海涵。