使用FutureTask而不是Callable有什么好处?

时间:2015-06-22 09:53:18

标签: java multithreading concurrency future

提交和轮询任务结果有两种方法

FutureTask futureTask = new FutureTask<String>(callable);
  1. 使用CallableFuture的组合,并在ExecutorService上提交。使用future.get()检索结果。

    Future future = service.submit(callable);
    
  2. 使用FutureTask。这将包装Callable,然后使用FutureTask检索结果。

    service.execute(task);
    
  3. 使用FutureTask优于Callable + Future组合的优势是什么?

3 个答案:

答案 0 :(得分:7)

几乎肯定都没有。快速浏览AbstractExecutorService的{​​{3}}会显示这些方法中的每一种都是辅助方法,最终将Callable / Runnable包裹在Future中。

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);
}

public Future<?> submit(Runnable task) {
    // ...
    RunnableFuture<Object> ftask = newTaskFor(task, null);
    execute(ftask);
    return ftask;
}

public <T> Future<T> submit(Runnable task, T result) {
    // ...
    RunnableFuture<T> ftask = newTaskFor(task, result);
    execute(ftask);
    return ftask;
}

public <T> Future<T> submit(Callable<T> task) {
    // ...
    RunnableFuture<T> ftask = newTaskFor(task);
    execute(ftask);
    return ftask;
}

答案 1 :(得分:5)

使用Future我们可以找出Callable任务的状态并获取返回的Object。它提供了get()方法,可以等待Callable完成然后返回结果。

Future提供cancel()方法来取消关联的Callable任务。有一个get()方法的重载版本,我们可以指定等待结果的时间,避免当前线程被阻塞更长时间是有用的。有isDone()和isCancelled()方法来查找关联的Callable任务的当前状态。

这是一个简单的Callable任务示例,它返回一秒钟后执行任务的线程的名称。我们使用Executor框架并行执行100个任务,并使用Future来获取提交任务的结果。

    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;

    public class MyCallable implements Callable<String> {

        @Override
        public String call() throws Exception {
            Thread.sleep(1000);
            //return the thread name executing this callable task
            return Thread.currentThread().getName();
        }

        public static void main(String args[]){
            //Get ExecutorService from Executors utility class, thread pool size is 10
            ExecutorService executor = Executors.newFixedThreadPool(10);
            //create a list to hold the Future object associated with Callable
            List<Future<String>> list = new ArrayList<Future<String>>();
            //Create MyCallable instance
            Callable<String> callable = new MyCallable();
            for(int i=0; i< 100; i++){
                //submit Callable tasks to be executed by thread pool
                Future<String> future = executor.submit(callable);
                //add Future to the list, we can get return value using Future
                list.add(future);
            }
            for(Future<String> fut : list){
                try {
                    //print the return value of Future, notice the output delay in console
                    // because Future.get() waits for task to get completed
                    System.out.println(new Date()+ "::"+fut.get());
                } catch (InterruptedException | ExecutionException e) {
                    e.printStackTrace();
                }
            }
            //shut down the executor service now
            executor.shutdown();
        }
    }

FutureTask的基础是Future接口的具体实现,并提供异步处理。它包含启动和取消任务的方法,以及可以返回FutureTask状态的方法,无论它是完成还是取消。我们需要一个可调用的对象来创建未来的任务,然后我们可以使用Java Thread Pool Executor来异步处理它们。

让我们看一下使用简单程序的FutureTask示例。

由于FutureTask需要一个可调用的对象,我们将创建一个简单的Callable实现。

    public class MyCallable implements Callable<String> {

    private long waitTime;

    public MyCallable(int timeInMillis){
        this.waitTime=timeInMillis;
    }
    @Override
    public String call() throws Exception {
        Thread.sleep(waitTime);
        //return the thread name executing this callable task
        return Thread.currentThread().getName();
    }

}

    import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class FutureTaskExample {

    public static void main(String[] args) {
        MyCallable callable1 = new MyCallable(1000);
        MyCallable callable2 = new MyCallable(2000);

        FutureTask<String> futureTask1 = new FutureTask<String>(callable1);
        FutureTask<String> futureTask2 = new FutureTask<String>(callable2);

        ExecutorService executor = Executors.newFixedThreadPool(2);
        executor.execute(futureTask1);
        executor.execute(futureTask2);

        while (true) {
            try {
                if(futureTask1.isDone() && futureTask2.isDone()){
                    System.out.println("Done");
                    //shut down executor service
                    executor.shutdown();
                    return;
                }

                if(!futureTask1.isDone()){
                //wait indefinitely for future task to complete
                System.out.println("FutureTask1 output="+futureTask1.get());
                }

                System.out.println("Waiting for FutureTask2 to complete");
                String s = futureTask2.get(200L, TimeUnit.MILLISECONDS);
                if(s !=null){
                    System.out.println("FutureTask2 output="+s);
                }
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }catch(TimeoutException e){
                //do nothing
            }
        }

    }

}

答案 2 :(得分:0)

FutureTask<T>类包含一个附加的“ done()”方法,因此我们可以覆盖done()方法,然后将FutureTask对象添加到ExecutorService,因此done() FutureTask立即完成时将调用该方法。