背景
Java异步编程的在实际开发中经常被用到,那么异步任务执行结束如何将结果通知到主线程或者其他任务呢?本文不探讨JUC包下的各类锁实现实现的任务同步或者通知。
一、Thread
狭隘的讲Java创建线程的方式只有一种,就是new Thread实例。Thread本身是Runnable的实现并且它定义了Runnable的field,所以支持自定义实现Runnable接口后,在new实例时构造Thread,最终一个新建的线程都需要通过调用start()执行。
这里有一点值得拿出来讲讲,如果直接执行Thread的run(),那么这时候并不是多线程的,它其实就是在主线程中执行了Runnable中定义的普通run方法。
由于Thread没有返回值,异步处理完的结果获取就有点麻烦。到了JDK1.5的时候,Callable和Future被引入,返回线程执行结果。
二、Future与FutureTask
讲到这里终于点题了,Future是一个接口,可以对Runnable或者Callable的task进行取消、判断是否取消、判断是否完成、获取执行结果;执行结果的获取是阻塞,直到task返回结果或者超时当前线程才会继续往下执行。
Future是一个接口,FutureTask是它的实现类,它的继承关系如下,
public class FutureTask<V> implements RunnableFuture<V> public interface RunnableFuture<V> extends Runnable, Future<V>
因此它可以作为Runnable被线程执行。
public static void main(String[] args) throws ExecutionException, InterruptedException { FutureTask<String> futureTask = new FutureTask<>(() -> { System.out.println(Thread.currentThread().getName()); // Thread-0 System.out.println("Runnable implements"); // Runnable implements }, null); new Thread(futureTask).start(); System.out.println(Thread.currentThread().getName()); // main String result = futureTask.get(); System.out.println(result); // null }
以上示例代码,输出内容顺序为 main-》Thread-0-》Runnable implements-》null 。最终输出null是因为FutureTask的构造函数中传入的就是null,如果有需要可以传值给异步任务处理,通过自定义Task类实现Runnable接口,把传值作为field给到Task处理。
小结
Future可以很容易的获得异步执行的结果,并且对任务进行一些操控;get等待结果时会阻塞,所以当任务之间有依赖关系的时候,一个任务依赖另一个任务的结果,可以用Future的get来等待依赖的任务完成的结果。
FutureTask是实现类,有Runnable的特性又有Future的特性,内部包的是Callable ,当然也有接受Runnable的构造器,只是会偷偷把Runnable转成Callable来实现能返回结果的方法。