您的位置:首页 > 编程语言 > Java开发

Java 多线程的设计模式之 Future

2017-04-23 18:37 651 查看
Future 设计模式核心原理图:



client端通过 FutureData 发送一个执行耗时操作的请求,FutureData 则直接返回一个回调接口的引用(Data 接口,用于返回获取到的真正结果),然后在 FutureData 内部再另起一个线程去执行真正的耗时操作。当 client 端 执行了获取结果的方法时,如果执行完成,则返回结果。如果还在执行中,则会进入线程等待状态,一直等到执行完成进行线程唤醒之后才能拿到结果。

这个设计模式在 Java JDK 中已经被实现了,这里先看一下自测代码,从最里层开始看:

自测 Future 模式实现

RealData 实现类

public class RealData {

private String data;

public RealData(String data) throws InterruptedException {
System.out.println("模拟数据加载中。。。");
TimeUnit.SECONDS.sleep(5);
this.data = data;
}

public String getResult() {
return data + " - real data";
}
}


上面流程图中标注的 RealData 也要实现 Data 接口,这里自己实现的话,其实也是可以省略的,自己写一个获取方法,效果也是一样的。这里模拟的是执行耗时操作。

FutureData 实现类

public class FutureData implements Data {

private boolean isReady;// 是否准备好了。 也就是耗时操作是否执行完成了。
private RealData data;// 真实数据操作对象

public synchronized void setRequest(RealData data) {
if (isReady) {
return;
}
this.data = data;
isReady = true;
notify();
}

@Override
public synchronized String getResult() throws InterruptedException {
if (!isReady) {
System.out.println("等待数据返回中。。。");
wait();
}
return data.getResult();
}
}


这里说一下,因为 RealData 的耗时操作直接在构造方法中写的,所以,当进入 setRequest 方法的时候,说明 RealData 已经实例化好了,也就是耗时操作已经执行完成了。这里 FutureData 也就是 RealData 的一个代理,包装着 RealData 的耗时操作过程。

wait() 和 notify() 方法 一定要配合 synchronize 关键字使用。


Client 端实现

public class Client {

public Data setRequest(String requestString) {
FutureData data = new FutureData();
new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("client run : " + System.currentTimeMillis());
data.setRequest(new RealData(requestString));// 当 RealData 的构造方法执行完成之后,才会进入到 setRequest的方法中。
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
return data;
}
}


调用代码

public class Main {
/**
* Futrue 设计模式,
* client端 发送某个耗时请求 - >  FutrueData 代理对象 先返回一个 假对象,然后其内部开启线程进行真实请求操作 -- >  RealData 真实处理对象,处理结束之后,回调数据给 代理对象,代理对象再进行通知 client端
*/

public static void main(String[] args) throws InterruptedException, ExecutionException {
Client client = new Client();
Data data = client.setRequest("this is test");
System.out.println("main : " + System.currentTimeMillis());
System.out.println(data.getResult()); // 会进入线程等待状态。

}
}


JDK 内部 Future 实现

上面也说了,这个设计模式 JDK 内部是已经实现了这个机制。 FutureTask 作为我们的代理类,它实现了 Runnable 接口,本身就作为一个独立的线程执行,但是它没有start 方法,必须配合线程池使用。因为 FutureTask 的传入对象是 Callable(接口) 类型,所以,我们的 RealData 类也需要实现 Callable 接口。真实数据最终会通过 Callable 接口的 call() 方法传递给 FutureTask 。

实现代码:

public class RealData_1 implements Callable<String> {

private String data;

public RealData_1(String data) {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("real data 1");
this.data = data;
}

@Override
public String call() throws Exception {
System.out.println("real data 1 call()");
return data;
}
}


调用方式:

public static void main(String[] args) throws InterruptedException, ExecutionException {
//        Client client = new Client();
//        Data data = client.setRequest("this is test");
//        System.out.println("main : " + System.currentTimeMillis());
//        System.out.println(data.getResult());

/**
* Java jdk 内置 FutureTask 是一个线程类,必须使用配合线程池调用。
*/
FutureTask futureTask = new FutureTask(new RealData_1("this is test jdk future"));
ExecutorService exe = Executors.newFixedThreadPool(1);

exe.submit(futureTask);

System.out.println(futureTask.get());

}


最后说一下线程池的 submit 方法和 execute 方法的区别:

submit 可以传入实现 Callable接口的实例对象。

submit 方法有返回值。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: