2011-02-10 47 views

回答

21

其實你是對的。這兩種方法是相同的。你通常不需要自己包裝它們。如果你是,你可能複製代碼的AbstractExecutorService:

/** 
* Returns a <tt>RunnableFuture</tt> for the given callable task. 
* 
* @param callable the callable task being wrapped 
* @return a <tt>RunnableFuture</tt> which when run will call the 
* underlying callable and which, as a <tt>Future</tt>, will yield 
* the callable's result as its result and provide for 
* cancellation of the underlying task. 
* @since 1.6 
*/ 
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) { 
    return new FutureTask<T>(callable); 
} 

未來RunnableFuture之間的唯一區別,是run()方法:

/** 
* A {@link Future} that is {@link Runnable}. Successful execution of 
* the <tt>run</tt> method causes completion of the <tt>Future</tt> 
* and allows access to its results. 
* @see FutureTask 
* @see Executor 
* @since 1.6 
* @author Doug Lea 
* @param <V> The result type returned by this Future's <tt>get</tt> method 
*/ 
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的目的是確保FutureTask實例不存在多於一個引用的可能方式。也就是說,執行者擁有這個實例。

+0

FutureTask.get()永遠不會拋出CancellationException,而Future.get()會拋出。它是否正確?請參閱http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/FutureTask.html#get(long,java.util.concurrent.TimeUnit)。 – 2013-03-01 19:56:38

13

Future只是界面。幕後,實施是FutureTask

您可以手動使用FutureTask,但您將失去使用Executor(合併線程,限制線程等)的優勢。使用FutureTask與使用舊的Thread和使用運行方法非常類似。

+1

FutureTask實現Future 和Runnable,爲什麼它不能提交給ExecutorService? – 2013-09-30 11:19:30

5

如果您想更改其行爲或稍後訪問其Callable,則只需使用FutureTask。對於99%的使用,只需使用Callable和Future。

38

FutureTask 這個類提供了base implementation of Future,使用方法的開始和取消計算

Future是接口

1

正如馬克,和其他人,正確地回答FutureFutureTaskExecutor有效的接口其工廠;這意味着應用程序代碼很少直接實例化FutureTask。爲了補充討論我提供表示在FutureTask被構造並直接使用的情況下,一個示例任何Executor外:

FutureTask<Integer> task = new FutureTask<Integer>(()-> { 
     System.out.println("Pretend that something complicated is computed"); 
     Thread.sleep(1000); 
     return 42; 
    }); 

    Thread t1 = new Thread(()->{ 
     try { 
      int r = task.get(); 
      System.out.println("Result is " + r); 
     } catch (InterruptedException | ExecutionException e) {} 
    }); 
    Thread t2 = new Thread(()->{ 
     try { 
      int r = task.get(); 
      System.out.println("Result is " + r); 
     } catch (InterruptedException | ExecutionException e) {} 
    }); 
    Thread t3 = new Thread(()->{ 
     try { 
      int r = task.get(); 
      System.out.println("Result is " + r); 
     } catch (InterruptedException | ExecutionException e) {} 
    }); 

    System.out.println("Several threads are going to wait until computations is ready"); 
    t1.start(); 
    t2.start(); 
    t3.start(); 
    task.run(); // let the main thread to compute the value 

這裏,FutureTask用作同步工具,像CountdownLatch或類似屏障原語。它可能已通過使用CountdownLatch或鎖和條件重新實施; FutureTask只是使它很好地封裝,不言自明,優雅,代碼少。

另請注意,FutureTask#run()方法必須在任何線程中顯式調用;那裏沒有Executor爲你做。在我的代碼中,它最終由主線程執行,但可以修改get()方法在第一個調用get()的線程上調用run(),因此第一個線程到達get(),並且它可以是T1,T2或T3中的任意一個所有剩餘線程的計算。

關於這個想法 - 第一個線程請求的結果會爲其他人計算,而併發嘗試將被阻止 - 是基於Memoizer的,請參閱「實踐中的Java併發」中第108頁的Memoizer Cache示例。

相關問題