Future和FutureTask在Java中有什么区别?

问题描述:

由于使用ExecutorService可以submit一个Callable任务并返回Future,为什么需要使用FutureTaskCallable任务和使用方法execute?我觉得他们都做同样的事情。Future和FutureTask在Java中有什么区别?

其实你是对的。这两种方法是相同的。你通常不需要自己包装它们。如果你是,你可能复制代码的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

Future只是界面。幕后,实施是FutureTask

您可以手动使用FutureTask,但您将失去使用Executor(合并线程,限制线程等)的优势。使用FutureTask与使用旧的Thread和使用运行方法非常类似。

+1

FutureTask实现Future 和Runnable,为什么它不能提交给ExecutorService? – 2013-09-30 11:19:30

如果您想更改其行为或稍后访问其Callable,则只需使用FutureTask。对于99%的使用,只需使用Callable和Future。

FutureTask 这个类提供了base implementation of Future,使用方法的开始和取消计算

Future是接口

正如马克,和其他人,正确地回答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示例。