- A+
概述
FutureTask
可取消的异步任务,提供Future
的基础实现,并实现了Runnable
接口。FutureTask
包含了取消与启动计算的方法,查询计算是否完成以及检索计算结果的方法。只有在计算完成才能检索到结果,调用get()
方法时如果任务还没有完成将会阻塞调用线程至到任务完成。一旦计算完成就不能重新开始与取消计算,但可以调用runAndReset()
重置状态后再重新计算。
类图
FutureTask
实现了RunnableFuture
接口,而RunnableFuture
接口扩展自Future
Runnable
接口,在创建FutureTask
时可以使用Callable
接口的实例或者Lambda表达式,也可以使用Runnable
的实例,但内部还是会使用适配器模式转换成Callable
实例类型。
创建第一个任务
使用Callable
创建一个FutureTask
实例:
FutureTask<Boolean> future = new FutureTask<>(new Callable<Boolean>() { @Override public Boolean call() throws Exception { return true; } });
通过new
一个对象的方法可以直接创建一个FutureTask
实例,如果直接调用run方法将直接在当前线程中运行,不会开启新线程。
使用ExecutorService
或者线程可以让FutureTask
进行托管进行,示例如下:
//托管给线程池处理Future<?> futureResult = Executors.newCachedThreadPool().submit(future);//托管给单独线程处理new Thread(future).start();
因为FutureTask
继承了Runnable
接口,所以它可以通过new Thread()
的方式进行运行,再由future
变量来检索结果值或者取消任务等操作,通过线程池托管的方式也可以适用。
取消任务
遇到特殊情况时需要对没有运行的或者已经运行的任务进行取消操作,这时可以调用cancel()
方法,取消方法有一个布尔类型的参数mayInterruptIfRunning
;当值为ture
时将尝试中断托管的线程(调用托管线程的interrupt
尝试中断)。
取消任务使用的是线程的中断
操作,如果任务是可取消的,在任务中存在阻塞线程的地方需要加上InterruptedException
的异常捕获来处理中断异常或者在任务可取消点使用Thread.currentThread().isInterrupted()
方法来判断任务是否已经发送的中断请求以正常取消任务。
检索结果值
使用FutureTask
的一个重要目的是为了能获取到任务的结果值,使用Callable
使用一个任务调用点时可以在任务中返回一个引用类型。
在FutureTask
内部使用outcome
变量存储Callable
的结果,调用FutureTask.get()
方法将检索结果值,但get()
方法也会阻塞调用线程直到任务执行完成或者取消。
get()
方法还可以通过设置超时时间来指定等待的时长,超过等待时间后将会抛出TimeoutException
异常。
总结
使用FutureTask
可以完成任务的取消、检查结果值,这两项也是FutureTask
的特色,但FutureTask
的底层还是托管给Thread
完成;相对于Thread
检查结果值会更新的方便,不再需要管理线程执行的状态与值。
在使用cancel
方法需要注意任务是否可以取消,在任务内部需要使用Thread.currentThread().isInterrupted()
检查中断状态并在Thread.sleep()
condition.wait()
Thread.join()
Thread.wait()
使用线程进行阻塞以及可中断的I/O操作方法中捕获InterruptedException
异常以避免不必要的情况发生,在大多数任务中是不应该被中断的,所以最好在可中断的任务中设置好检查点;在任务线程会被阻塞点捕获InterruptedException
异常,根据情况判断是否需要取消任务。