前言
创建线程的两种方式主要有继承 Thread 类 或 实现 Runnable 接口,重写 run 方法,无论我们使用 Thread 还是 Runnable 新建线程,它都无法返回结果值。自从 Jdk1.5 开始,官方提供了 Callable 和 Future, 通过他们就可以获得任务的执行结果。其中 FutureTask 则作为 Future 的实现类。
下面是他们的关系类图:
Runnable
Thread 类本身也实现了 Runnable 接口,Runnable 接口源码如下:
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
因此,可以看得出,不管我们是实现 Runnable 接口,还是继承 Thread 类,run 方式的返回类型都是 void,,没有任务返回接口,并且无法抛出异常。
如果需要获取任务返回结果怎么办?Callable 便应运而生了。
Callable
Callable 是一个接口,一个函数式接口,也是一个泛型接口,只有一个 call 方法,在 call 方法里面编写我们要执行的代码就行了。返回的就是执行的结果。Callable 可以看作是 Runnable 接口的补充,和 Runnable 的区别就是它有返回任务结果,并且可以抛出异常,一般配合 ThreadPoolExecutor 使用。
@FunctionalInterface
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
Future
Future 是个接口,它可以对具体的 Runnable 或者 Callable 任务进行取消、判断任务是否已取消、查询任务是否完成、获取任务结果。接口里面有以下几个方法。注意两个 get 方法都会阻塞当前调用 get 的线程,直到返回结果或者超时才会唤醒当前的线程。
public interface Future<V> {
//取消任务的执行,任务已经完成或者已经被取消时,调用此方法,会返回false
boolean cancel(boolean mayInterruptIfRunning);
//判断任务是否被取消
boolean isCancelled();
//判断任务是否完成(正常完成、异常以及被取消,都将返回true)
boolean isDone();
//阻塞地获取任务执行的结果
V get() throws InterruptedException, ExecutionException;
//在一定时间内,阻塞地获取任务执行的结果
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
但由于 Future 只是一个接口,为了使用它里面的功能,我们必须使用它的间接继承类 FutureTask。
FutureTask
FutureTask 是真正工作的处理类,FutureTask 实现了 RunnableFuture 接口,而 RunnableFuture 继承了 Runnable 与 Future 接口,因此 FutureTask 既可以作为一个Runnable 被 Thread 执行,也可以获取到 Future 异步计算的结果。
它的两个构造器:
/**
* Creates a {@code FutureTask} that will, upon running, execute the
* given {@code Callable}.
*
* @param callable the callable task
* @throws NullPointerException if the callable is null
*/
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
/**
* Creates a {@code FutureTask} that will, upon running, execute the
* given {@code Runnable}, and arrange that {@code get} will return the
* given result on successful completion.
*
* @param runnable the runnable task
* @param result the result to return on successful completion. If
* you don't need a particular result, consider using
* constructions of the form:
* {@code Future<?> f = new FutureTask<Void>(runnable, null)}
* @throws NullPointerException if the runnable is null
*/
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
两个构造方法,一个接收 Callable 的参数实例,另一个接收 Runnable 的参数实例。
当传入的参数是 Runnable 时,通过 Executors.callable(runnable, result) 方法将其转成 Callable 类型,返回值类型为 V(指定的泛型类型)
FutureTask 代码案例
public class Test {
static class MyTask implements Callable<Integer> {
@Override
public Integer call() throws Exception {
Thread.sleep(2000);
return 1;
}
}
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newCachedThreadPool();
MyTask myTask = new MyTask();
FutureTask<Integer> futureTask = new FutureTask<Integer>(myTask);
executor.submit(futureTask);
executor.shutdown();
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println("主线程在执行任务");
try {
if(futureTask.get()!=null){
System.out.println("task运行结果"+futureTask.get());
}else{
System.out.println("future.get()未获取到结果");
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("所有任务执行完毕");
}
}