我们知道创建线程的方式有两种,一种是实现Runnable接口,另一种是继承Thread,但是这两种方式都有个缺点,那就是在任务执行完成之后无法获取返回结果,那如果我们想要获取返回结果该如何实现呢?java 为我们提供了 Callable 接口和 Future ,从JAVA SE 5.0开始引入了Callable和Future,通过它们构建的线程,在任务执行完成后就可以获取执行结果,这就是线程的第三种方式,那就是实现Callable接口。
Callable
@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; }
该接口声明了一个名称为 call() 的方法,同时这个方法可以有返回值 V,也可以抛出异常。无论是 Runnable 接口的实现类还是 Callable 接口的实现类,都可以被 ThreadPoolExecutor 或 ScheduledThreadPoolExecutor 执行,ThreadPoolExecutor或ScheduledThreadPoolExecutor都实现了ExcutorService 接口,而因此 Callable 需要和Executor 框架中的 ExcutorService 结合使用,我们先看看 ExecutorService 提供的方法
public Future<?> submit(Runnable task) { return schedule(task, 0, NANOSECONDS); } public <T> Future<T> submit(Runnable task, T result) { return schedule(Executors.callable(task, result), 0, NANOSECONDS); } public <T> Future<T> submit(Callable<T> task) { return schedule(task, 0, NANOSECONDS); }
- 第一个方法:submit提交一个实现Runnable接口的任务,并且返回封装了异步计算结果的Future。
- 第二个方法:submit提交一个实现Runnable接口的任务,并且指定了在调用Future的get方法时返回的result对象。
- 第三个方法:submit提交一个实现Callable接口的任务,并且返回封装了异步计算结果的Future。
因此我们只要创建好我们的线程对象(实现Callable接口或者Runnable接口),然后通过上面3个方法提交给线程池去执行即可。还有点要注意的是,除了我们自己实现Callable对象外,我们还可以使用工厂类Executors来把一个Runnable对象包装成Callable对象。Executors工厂类提供的方法如下
public static <T> Callable<T> callable(Runnable task, T result) { if (task == null) throw new NullPointerException(); return new RunnableAdapter<T>(task, result); } public static Callable<Object> callable(Runnable task) { if (task == null) throw new NullPointerException(); return new RunnableAdapter<Object>(task, null); }
Future
Future接口是用来获取异步计算结果的,说白了就是对具体的Runnable或者Callable对象任务执行的结果进行获取(get()),取消(cancel()),判断是否完成等操作。Future接口的源码:
public interface Future<V> { // 如果任务还没有开始,执行 cancel() 方法将返回false,如果任务已经启动,执行 cancel(true) 方法将 // 已中断执行此任务线程的方式来试图停止任务,如果停止成功,返回 true,当任务已经启动,执行 // cancel(false) 方法将不会对正在执行的任务线程产生影响(让线程正常执行到完成),此时返回 false // 当任务已经完成,执行 cancel() 方法将返回 false ,mayInterruptlfRunning 参数表示是否中断执行中的 // 线程,通过方法分析我们也知道实际上 Future 提供了 3 中可能:1.能够中断执行中的任务,2.判断 // 任务是否执行完成,3.获取任务执行完成后的结果,但是 Future 是一个接口,我们无法直接创建对象 // 因此就需要其实现类 FutureTask boolean cancel(boolean mayInterruptIfRunning); // 如果任务完成前被取消,则返回 true boolean isCancelled(); // 如果任务执行结束,无论是正常结束或是中途取消还是发生异常,都返回 true boolean isDone(); // 该方法用于获取异步执行的结果,如果没有结果返回,该方法会一直阻塞直到任务执行完成 V get() throws InterruptedException, ExecutionException; // 获取异步执行结果,如果没有结果返回,此方法会一直阻塞,但是会有时间的限制 // 如果阻塞时间超过设定的 timeout 时间,该方法会抛出异常 V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }