该模式的主要思想是:当你调用方法后,会开启新线程运行方法体,方法体可能不会马上计算出真正结果,但是方法会先给你返回一个值,让你不必一直守在这里等待,你可以去办其它事情,等未来真正需要方法体计算出的结果的时候,再尝试获取。
非java.util.Concurrent包实现
首先创建一个接口IData:
|
|
FutureData和RealData都实现了这个接口。一开始我们需要的是RealData,但是RealData并没有创建好,没关系,此时并不着急使用RealData提供的数据,这时候返回一个FutureData,它就像RealData的代理一样,等“有朝一日”,需要真正数据的时候,就会用FutureData代理RealData的方法,如果这个时候RealData还是没有生成数据,那么就只有等待了。按照这个思路,接下来实现RealData,在构造器中添加睡眠函数模拟创建需要时间消耗的过程:
|
|
FutureData的实现:
|
|
在main函数的执行过程中,setData总是先行调用,第一次调用的时候ready为false,所以等待RealData实例的创建,如果这个创建一直没有完成,那么setData()就一直拿着锁,只要调用getContent()就会一直处于waiting状态。一旦RealData实例创建完成,ready为true此时FutureData的getContent()返回真正的结果。
代理客户端执行类:
|
|
main方法:
|
|
至此,一个简单的Future模式就手工完成了。
接下来把以上代码改写为java.util.Concurrent包实现。
java.util.Concurrent包实现
在Java中java.util.Concurrent包提供了相应的类,对一些核心操作进行了封装,例如不需要FutureData手动写同步方法,只需要去继承FutureTask类,此类是一个继承了Future接口的标准类。
创建FutureData02类,它是前文中FutureData的改写:
|
|
和之前实现FutureData类不同的地方在于,不需要写同步方法,要想获得RealData实例,必须首先调用FutureTask提供的get()。
接着就是代理客户端执行类的实现:
|
|
Callable可以理解为有返回值的Runnable,想想之前的get(),然后就能体会Callable返回的实例realData。在最后启动线程的时候,传入的是future02,实际上它的父类FutureTask实现了Runnable接口。
小结
这里用两种方式,实现了简单的Future模式。
其他问题
这里出现了内部匿名类引用外部方法的局部变量必须声明为final的问题,除此之外,引用外部作用域内的局部变量和外部方法的参数也要声明为final,Java 8增加的Effectively final功能聚焦的也是这个问题。可以参考链接:
也可利用可变对象,构造大小为1的数组来规避。