В чем преимущество использования FutureTask над Callable?

Существует два подхода к представлению и опросу задачи

FutureTask futureTask = new FutureTask<String>(callable);
  1. Используйте комбинацию Callable и Future и отправляйте в ExecutorService. Получить результат с помощью future.get().

    Future future = service.submit(callable);
    
  2. Используйте FutureTask. Это приведет к завершению работы Callable и последующему извлечению результата с помощью FutureTask.

    service.execute(task);
    

В чем преимущество использования FutureTask over Callable + Future?

Ответ 1

Почти наверняка нет. Быстрый просмотр GrepCode в AbstractExecutorService показывает, что каждый из этих методов - это просто вспомогательные методы, которые в конечном итоге завершают 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;
}

Ответ 2

Используя Future, мы можем узнать статус задачи Callable и получить возвращаемый объект. Он предоставляет метод get(), который может дождаться завершения Callable и затем вернуть результат.

Будущее предоставляет метод 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
            }
        }

    }

}