FutureTask 使用详解

  • A+
所属分类:Java 多线程

概述

FutureTask 可取消的异步任务,提供Future的基础实现,并实现了Runnable接口。FutureTask包含了取消与启动计算的方法,查询计算是否完成以及检索计算结果的方法。只有在计算完成才能检索到结果,调用get()方法时如果任务还没有完成将会阻塞调用线程至到任务完成。一旦计算完成就不能重新开始与取消计算,但可以调用runAndReset()重置状态后再重新计算。

类图

FutureTask 使用详解

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异常,根据情况判断是否需要取消任务。

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: