Future模式
在多线程开发中,Future模式是经常使用到的一种设计模式,其核心思想就是异步调用。当我们要调用一个函数方法fun时,fun的执行很慢,如果我们不需要着急知道fun的结果时,我们可以让调用者直接返回去做其他的任务,让fun在后台继续执行。当我们需要用到fun的结果时再去取。
Future模式就好像我们在网上购物,我们只要负责下单就可以做自己的事情,而不需要傻傻的等待商品到来。我们只需要在商品送货上门到来的时候提供购买凭证即可。流程图如下:
客户端发起一次请求给服务端,虽然服务端执行这次请求需要花费很久,但是它会立即返回一个假的结果给客户端,此时客户端没有着急去处理返回的结果,而是去做了其他的事情;再完成了其他任务时再去处理返回的结果,而此时服务端已经能返回真实的结果了。
interface Data {
public String getData();
}
class FutureData implements Data {
RealData realData = null;//包装了RealData
boolean isReady = false;
public synchronized void setData(RealData realData) {
if (isReady) {
return;
}
this.realData = realData;
isReady = true;
notifyAll();//通知getData已经注入realData
}
@Override
public synchronized String getData() {
while (!isReady) {//realData没有被注入被一直等待
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return realData.result;
}
}
class RealData implements Data {
final String result;
public RealData(String para) {
StringBuffer sb = new StringBuffer();
//构造数据中
for (int i = 0; i < 3; i++) {
sb.append(para);
try {
//表示构造数据很漫长
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
result = sb.toString();
}
@Override
public String getData() {
return result;
}
}
public class Client {
public Data request(final String para) {
final FutureData future = new FutureData();
new Thread() {
public void run() {
//RealData构建很慢
RealData realData = new RealData(para);
future.setData(realData);
}
}.start();
return future;//FutureData马上被返回
}
public static void main(String[] args) {
Client client = new Client();
Data data = client.request("name");//马上返回一个假的data
System.out.println("请求完毕,时间:"+System.currentTimeMillis()/1000);
//System.out.println("futuredata:"+data.getData()+",时间:"+System.currentTimeMillis()/1000);
try {
TimeUnit.SECONDS.sleep(7);
} catch (InterruptedException e) {
e.printStackTrace();
}
//返回真实的data
System.out.println("realdata:"+data.getData()+",时间:"+System.currentTimeMillis()/1000);
}
}
/*
请求完毕,时间:1508380112
realdata:namenamename,时间:1508380119
如果把注释去掉:
请求完毕,时间:1508380253
futuredata:namenamename,时间:1508380259
realdata:namenamename,时间:1508380266
*/
由输出可以知道,如果需要马上对数据进行处理,则会造成线程等待。
JDK中的Future模式
JDK中的Future模式的基本结构如下所示:
我们需要关注的是RunnableFuture,它实现了Future接口(就像上文描述的一个订单,通过它可以获得数据)和Runnable接口,FutureTask是具体的实现,get用于获得真实数据,run用于真实数据构造;他会委托Sync类实现,而Sync类回调用Callable接口实现。Callable接口只有一个call方法,它会返回我们需要构造的实际数据。
class RealData implements Callable<String> {
private String para;
public RealData(String para) {
this.para = para;
}
@Override
public String call() throws Exception {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 3; i++) {
sb.append(para);
TimeUnit.SECONDS.sleep(1);
//System.out.println(sb);
}
return sb.toString();
}
public class FutureMain {
}
public static void main(String[] args) throws InterruptedException,
ExecutionException {
//构造FutureTask
FutureTask<String> futureTask = new FutureTask<>(new RealData("a"));
ExecutorService service = Executors.newFixedThreadPool(1);
//执行FutureTask相当于上例中request("name")
service.execute(futureTask);
System.out.println("请求提交");
long start = System.currentTimeMillis();
try {
//模拟主线程做了一下其他的事情
System.out.println("做了其他的事情");
TimeUnit.SECONDS.sleep(5);
System.out.println("其他的事情做完了");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("结果为" + futureTask.get());
long end = System.currentTimeMillis();
System.out.println("用时:" + (end - start) / 1000);
}
}
/*
请求提交
做了其他的事情
其他的事情做完了
结果为aaa
用时:5
*/
从程序用时可以看出,主线程和Future之间分工明确,异步执行。
除了基本功能外,Future还提供了一下简单的控制方法:
//取消任务
boolean cancel(boolean mayInterruptIfRunning);
//任务是否取消
boolean isCancelled();
//任务是否完成
boolean isDone();
//取得返回对象
V get() throws InterruptedException,ExecutionException;
//取得返回对象,设置超时时间
get(long timeout,TimeUnit unit);
如果对未取得真实数据的data使用get会使得线程等待,可以为get设置一个时间或者使用isDone来判断data是否取得真实数据。