Callable
Runnable 封装一个异步运行的任务,可以把它想象成为一个没有参数和返回值的异步方法。
Runnable runnable = new Runnable() {
@Override
public void run() {
}
};
runnable.run();
Callable 和 Runnable 类似,但是有返回值,Callable 接口只有一个方法 call。
public interface Callable<V>{
V call() throw Exception;
}
Callable<Integer> callable = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return null;
}
};
Integer result = callable.call();
参数类型是返回的类型,例如 Callable<Integer> 表示一个最终返回 Integer 对象的异步计算。
可以像上例一样直接执行 call() 方法,然后获取执行结果,但是,除了可以获取最终结果之外,你不能对它进行任何操作或干预。
若想监控这个计算是否执行完毕,或者试图中断它,有以下两种方案:
1.借助FutureTask包装器
将 Callable 实例作为构造参数提交给一个 FutureTask 实例,然后调用 FutureTask 实例的 run() 方法
FutureTask<Integer> task = new FutureTask<>(callable);
task.run();
在 run() 方法内部,callable 对象的 call() 方法将会被调用,计算完成后,FutureTask 会将结果保存下来,可以通过 get 方法获取计算结果
public V get()
public V get(long timeout, TimeUnit unit)
第一个 get 方法的调用被阻塞,直到计算完成。
第二个 get 方法会等待指定的时间,若时间之内计算仍然没有完成,则抛出 TimeoutException 异常。
如果计算被中断,两个 get 方法都将抛出 InterruptedException 异常。
2.将Callable提交给线程池
利用 Executors 类的工厂方法获取线程池(请参考我的另一篇文章:Executor 执行器),然后调用 submit 方法提交计算任务。
ExecutorService executor = Executors.newCachedThreadPool();
Future<Integer> future = executor.submit(callable);
submit 方法将返回一个 Future 的 实例对象,该对象包含了任务的计算结果,实际上,在上一个方案中的 FutureTask 类即是 Future 的一个实现:
FutureTask 实现了 RunnableFuture 接口
public class FutureTask<V> implements RunnableFuture<V>
而 RunnableFuture 接口继承于 Runnable, Future 接口
public interface RunnableFuture<V> extends Runnable, Future<V>
Future
Future 接口共包含五个抽象方法:
- cancel(boolean mayInterrupt) :可以用此方法取消计算,如果计算还没有开始,它将被取消且不再开始;若计算已经处于运行之中,且 mayInterrupt 参数为 true,则计算将被中断:这将导致对这个任务 get 方法的调用抛出 InterruptedException 异常。
- isCancelled() :如果该计算在完成之前被取消则返回 true。
- isDone() :如果任务已经完成则返回 ture (正常结束,被取消,抛出异常都视为任务完成)
- get() :获取计算结果,此方法阻塞调用,直到计算完成。
- get(long timeout, TimeUnit unit) :获取计算结果,如果计算已完成则直接返回,若没完成则等待指定的时间,时间之内计算仍没有完成,则抛出 TimeoutException 异常
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get();
V get(long timeout, TimeUnit unit)