FutureTash是J.U.C里面的,但是它不是AQS的子类,但是这个类对线程结果的处理很值得我们学习和在项目中使用。
创建一个线程通常有两种方式,一种是直接继承Thread、另一种是直接实现Runnable,这两种方式有一个共同的缺陷就是在执行完任务之后无法获取执行结果。从java1.5开始就提供了Callable和Future,通过他们可以在任务完成之后得到任务的执行结果。
Callable与Runnable对比
- Runnable接口代码简单,只有一个方法run()
- Callable是一个泛型接口,有一个call函数,call的返回类型就是创建Callable的类型,功能更强大一些。
Future接口
对于一个具体的Callable和Runnable任务,Future可以进行取消、查询一个任务是否被取消、查询是否完成、获取结果等等。通常线程都是异步执行的,因此不可能从其他线程中得到返回值。Future可以监视目标线程call的情况,当调用Future的get()时就可以获得它的结果,这时线程可能不会直接完成,当前线程开始阻塞,直到call方法结束并返回结果,线程才继续执行。总结一句话,Future可以得到其他线程任务方法的结果返回值。
FutureTask类
FutureTask类的父类是RunnableFuture,而RunnableFuture继承了Runnable和Future两个接口。由此可以知道FutureTask最终也是执行Callable类型的任务。如果构造函数参数是Runnable的话,会自动转换成Callable类型。FutureTask实现了两个接口(Runnable和Future),所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。RunnableFuture继承了Runnable接口和Future接口,而FutureTask实现了RunnableFuture接口。
那么,这个组合的使用有什么好处呢?假设有一个很费时的逻辑,需要计算并返回计算结果,同时这个值不是马上需要,那么就可以使用这个组合,用另外一个线程去计算返回值,而当前线程在使用这个返回值之前可以做其他操作,等到需要这个返回值时可以通过Future得到。
Future使用举例如下:
@Slf4j
public class FutureExample {
static class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
log.info("do something in callable");
Thread.sleep(5000);
return "callable done";
}
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executorService = Executors.newCachedThreadPool();
Future<String> future = executorService.submit(new MyCallable());
log.info("do something in main");
Thread.sleep(1000);
String result = future.get(); // 如果call任务没有结束,则会阻塞在这里
log.info("result: {}", result);
}
}
FutureTask举例
@Slf4j
public class FutureTaskExample {
public static void main(String[] args) throws InterruptedException, ExecutionException {
FutureTask<String> futureTask = new FutureTask<String>(new Callable<String>() {
@Override
public String call() throws Exception {
log.info("do something in callable");
Thread.sleep(5000);
return "callable done";
}
});
new Thread(futureTask).start();
log.info("do something in main");
Thread.sleep(1000);
String result = futureTask.get();
log.info("result: {}", result);
}
}