您好,登錄后才能下訂單哦!
小編這次要給大家分享的是詳解java中Future與FutureTask,文章內容豐富,感興趣的小伙伴可以來了解一下,希望大家閱讀完這篇文章之后能夠有所收獲。
Future與FutureTask都是用于獲取線程執行的返回結果。下面我們就對兩者之間的關系與使用進行一個大致的介紹與分析
一、Future與FutureTask介紹:
Future位于java.util.concurrent包下,它是一個接口
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個方法,下面介紹一下每個方法的作用:
cancel方法用來取消任務,取消成功則返回true,取消失敗則返回false。參數mayInterruptIfRunning設置為false,表示不允許在線程運行時中斷,設置為true則表示允許。具體可分為以下三種情況:
1、如果任務已經完成,則無論mayInterruptIfRunning為true還是false,都返回false,這是因為你要取消的任務已經完成,則認為取消任務失敗;
2、如果任務正在執行,則無論mayInterruptIfRunning為true還是false,都返回true。只不過mayInterruptIfRunning為true時線程會被中斷,false時線程不會被中斷會執行完。
3、如果任務還沒有執行,則無論mayInterruptIfRunning為true還是false,都返回true。
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使用與分析
1、使用Future時,我們需要實現Callable接口,并通過ExecutorService接口的submit方法獲取返回的Future對象,
2、使用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對象,所以我們具體分析下上面代碼中的三種情況:
1、如果你傳入的是自己實現的Runaable類或者Callable類,那么sumbit方法自然會幫你自動封裝為FutureTask對象,運行后通過Future對象獲取結果。
2、你傳入的已經是個自己構造的FutureTask對象,由于FutureTask其實是實現了Runnable接口的,它本身就是個Runaable實現類, sumbit方法還是會將它視為Runnable類來進行封裝,并最終會執行FutureTask自己的run方法,一系列實現都在你傳入的FutureTask對象內完成,所以你可以直接通過自己構建的FutureTask獲取結果;
3、自己單獨聲明線程運行,跟第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去實現,沒有什么區別;
看完這篇關于詳解java中Future與FutureTask的文章,如果覺得文章內容寫得不錯的話,可以把它分享出去給更多人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。