与 Callable 相比,使用 FutureTask 有什么优势?

2022-09-01 16:14:29

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

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

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

    service.execute(task);
    

使用比+未来组合的优势是什么?FutureTaskCallable


答案 1

几乎可以肯定根本没有。快速浏览一下 GrepCode 的显示,这些方法中的每一个都只是帮助器方法,最终将 / 包装在 a 中。AbstractExecutorServiceCallableRunnableFuture

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,我们可以找出可调用任务的状态并获取返回的对象。它提供了 get() 方法,该方法可以等待 Callable 完成,然后返回结果。

Future 提供 cancel() 方法来取消关联的可调用任务。有一个get()方法的重载版本,我们可以指定等待结果的时间,避免当前线程被阻塞更长的时间是很有用的。有 isDone() 和 isCancelled() 方法可以找出关联的 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线程池执行器异步处理这些任务。

让我们用一个简单的程序来看看FutureTask的例子。

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

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

    }

}

推荐