本节内容摘自《Java异步编程实战》中的一小节。
前言
本篇主要讲解如何使用JDK中的Future 实现异步编程,包括如何使用FutureTask 实现异步编程以及内部实现原理以及FutureTask 的局限性。
JDK 中的Future
在Java并发包(JUC包)中Future 代表着异步计算的结果,Future中提供了一些方法用来检查计算结果的完成,还提供了同步等待任务执行完成的方法,以及获取结果的方法。当计算结果完成时,通过提供的get系列方法来获取结果,如果使用了不带超时时间的方法来获取结果,则在计算结果完成前,调用线程会一直阻塞,另外计算任务是可以通过cancel 方法来取消的,前提就是任务未执行完毕。
首先我们来看下Future 接口的结构图
JDK中的FutureTask
FutureTask 代表了一个可被取消的异步计算任务,该类实现了RunnableFuture接口,既包含Runnable 功能,也包含Future 功能。提供了启动和取消任务,查询任务是否完成,获取计算结果等。
FutureTask 任务的执行结果只有当任务完成以后才能获取。并且只有通过get系列方法获取。当计算未完成时,get方法会阻塞等待结果,任务一旦被执行完成,除非运行的时候用了runAndReset 方法,否则任务不能被重启。FutureTask 中的任务可以是Callable 类型的,也可以是Runnable 类型的,FutureTask 类型的任务可以被提交到线程池。
FutureTask 实现了Future 接口的所有方法,并且实现了Runnable ,所以其是可执行任务,可以投递到线程池或者由线程来执行。
FutureTask 中变量state 是一个使用volatile 关键字修饰的(用来解决内存可见性防止指令重排)int变量。用来记录任务的状态。
FutureTask的局限性
FutureTask 虽然提供了用来检查任务是否完成,等待任务执行结果,获取任务执行结果的方法,但是这些特色并不足以让我们写出简洁的并发代码,比如它并不清楚的表达出多个FutureTask 之间的关系,另外为了从Future获取结果,我们必须调用get()方法,而该方法还是会在任务执行完毕前阻塞调用线程,这明显不是我们想要的。
我们真正想要的是:
1、可以将两个或多个异步计算结合在一起变成一个,这包括两个或多个异步计算是独立的时候,或者第二个异步计算依赖于第一个异步计算。
2、对反应式编程的支持,也就是当任务计算完成后进行通知,并且可以将计算结果作为下一步计算动作的参数,而不是仅仅提供调用线程以阻塞的方式获取结果。
3、可以通过编程的方式手动设置Future 的结果,FutureTask 则不可用让用户通过函数设置其计算结果,而是其任务内部进行设置。
4、可以等多个Future 对应的计算结果都出来后做一些事情。
为了克服FutureTask的局限性,以及满足我们对异步编程的需要,JDK8中提供了CompletableFuture,CompletableFuture是一个可以通过编程方式显式的设置计算结果和状态以便让任务结束的Future,本书后面章节我们会具体讲解。
总结
推荐书目:《Java异步编程实战》