深入并发之(三) FutureTask与Future之写线程池的时候到底需不需要自己包装FutureTask

写线程池的时候到底需不需要自己包装FutureTask

一般来说,我们会写一个Runnable接口的实现用来包装我们需要异步执行的内容。但是,Runnable是没有实现的,有些时候,我们需要执行的内容是一个有返回值的方法或者需要抛出异常,这个时候,我们就需要使用Callable接口。

简单来说,callable可以实现所有Runnable能够实现的功能。

由于我们需要在线程异步执行完成后返回一个结果,但是由于这个执行是异步的,那么,我们是没有办法立刻拿到结果的,这时Future方式就出现了。

Future实际上一个承诺,当我们调用时,返回一个Future对象,这个返回是立刻的,当线程的异步任务完成时,我们可以通过方法Future.get()来获取真正的返回值。

在代码的编写过程中,我们可能见过以下两种例子

示例1

FutureTask<String> task = new FutureTask<String>(new MyCallable());
ExecutorService executorService = Executors.newFixedThreadPool(5);

executorService.submit(task);

String s = task.get();

System.out.println(s);

executorService.shutdown();

示例2

MyCallable callable = new MyCallable();

ExecutorService executorService Executors.newFixedThreadPool(5);

Future<String> future executorService.submit(callable);

String s = future.get();

System.out.println(s);

executorService.shutdown();

我们发现这两种方式实际上是相同的结果,这是我们就会十分迷惑,FutureTask与Future有什么区别?

首先,我们需要了解,Future实际上是一个接口,这个接口中有如下方法。

futureMethod

FutureTask实际上是实现了接口RunnableFuture,而RunnableFuture继承了Runnable, Future

public class FutureTask<V> implements RunnableFuture<V>

public interface RunnableFuture<V> extends Runnable, Future<V> 

所以,我们知道实际上FutureTask实际上既是一个Future也是一个Callable,因此,它可以作为线程池的一个任务,通过submit(Callable)方法提交给线程池进行处理。

一般来说,我们不需要对Callable进行包装,也就是上面示例1的方式并不是我们通常应该选择的方式。因为,实际上在线程池中已经将Callable包装为FutureTask了。

这里我们从源码中分析一下我们给线程池执行的Callable到底是怎么执行的?

首先,我们需要查看submit方法

查看ThreadPoolExecutor类,没有找到submit方法,那么这个方法一定是在父类中

public class ThreadPoolExecutor extends AbstractExecutorService

接下来我们查看抽象类AbstractExecutorService

public <T> Future<T> submit(Callable<T> task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<T> ftask = newTaskFor(task);
    execute(ftask);
    return ftask;
}

这里我们注意到在task执行前,有一个方法newTaskFor的调用,这个方法返回了RunnableFuture类的对象

public interface RunnableFuture<V> extends Runnable, Future<V> 

FutureTask实际上是这里的RunnableFuture的接口

protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
    return new FutureTask<T>(callable);
}

我们看到newTaskFor方法,实际上返回的就是FutureTask,因此,我们根本无需再将Callable包装为FutureTask,这个工作实际上重复的。

接下来的一篇中,我们将会分析真正执行任务的方法execute(ftask);

原文地址:https://www.cnblogs.com/qmlingxin/p/9631874.html