一、什么是Future模型: 

  Future模式是多线程开发中非常常见的一种设计模式,它的核心思想是异步调用。这类似我们网上订餐订座,只要一个电话,客服就告诉我们已经预定成功(实际客服MM啥都还没做好),但是我们这时就从家里出发,同时客服MM也在准备着我们的位置,当我们到了的时候菜和位置也都准备好了。或者说更形象的是我们发送Ajax请求的时候,页面是异步的进行后台处理,用户无需等待请求的结果,可以继续浏览或操作其他内容。

              

  客户端发送一个长时间的请求,服务端不需等待该数据处理完成便立即返回一个伪造的代理数据(相当于商品订单,不是商品本身),用户也无需等待,先去执行其他的若干操作后,再去调用服务器已经完成组装的真实数据。该模型充分利用了等待的时间片段。

二、Future模式的核心结构:

        

  Main:启动系统,调用Client发出请求;

  Client:返回Data对象,理解返回FutureData,并开启ClientThread线程装配RealData;

  Data:返回数据的接口;

  FutureData:Future数据,构造很快,但是是一个虚拟的数据,需要装配RealData;

  RealData:真实数据,构造比较慢。

三、Future模式的代码实现:

  main(此类主要调用FutureClient的getRequset方法去返回数据):

public class Main {
public static void main(String[] args) {
FutureClient futureClient = new FutureClient();
System.out.println("发送请求...");
Data data = futureClient.getRequset("待处理字符串。");
System.out.println("请求完毕...");
String result = data.getRequest();
System.out.println("返回的结果:"+result);
}
}

  FutureClient(该类在接受到用户请求后很快就能返回虚拟数据 futureData,本身启动一个线程去获取真实数据):

public class FutureClient {
public Data getRequset(final String queryStr){
//初始化代理对象,先返回给客户端
final FutureData futureData = new FutureData();
//启动一个新的线程去加载真实的数据,传递给这个代理对象
new Thread(new Runnable() {
public void run() {
//此线程去加载真实对象,然后传递给代理对象
RealData realData = new RealData(queryStr);
futureData.setRealData(realData);
}
}).start();
System.out.println("代理对象返回:"+futureData);
return futureData;
}
}

  Data接口(用于FutureData和RealData统一化接口):

public interface Data {
public String getRequest();
}

  FutureData(该类是Future模式的关键,它实际是真实数据RealData的代理,封装了获取RealData的等待过程实际返回的是真实的数据。):

public class FutureData implements Data{
private RealData realData;
private boolean isReady = false; public synchronized void setRealData(RealData realData){
//如果已经装载完毕则直接返回
if(isReady){
return;
}
//如果未装载,进行装载真实数据
this.realData = realData;
isReady = true;
//通知
notify();
} public synchronized String getRequest() {
//如果未装载好一直处于阻塞状态
while (!isReady){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//装载好直接返回数据即可
return this.realData.getRequest();
}
}

  RealData(RealData装载数据较慢,这里使用Sleep(5000)模拟复杂业务逻辑。):

public class RealData implements Data{
private String result; public RealData(String queryStr){
System.out.println("根据参数: "+queryStr+" 进行查询,这是一个很耗时的操作!");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("装载完毕,获取结果");
result = "the Result!!!";
} public String getRequest() {
return result;
}
}

注意:

  FutureData是对RealData的包装,是dui真实数据的一个代理,封装了获取真实数据的等待过程。它们都实现了共同的接口,所以,针对客户端程序组是没有区别的;

  客户端在调用的方法中,单独启用一个线程来完成真实数据的组织,这对调用客户端的main函数式封闭的;

  因为咋FutureData中的notifyAll和wait函数,主程序会等待组装完成后再会继续主进程,也就是如果没有组装完成,main函数会一直等待。

四、JDK原生包实现:

  JDK内置的Future主要使用到了Callable接口和FutureTask类。

  Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其他线程执行的任务。Callable接口的定义如下:

public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}

  Callable的类型参数是返回值的类型。例如:

Callable<Integer>表示一个最终返回Integer对象的异步计算。

  Future保存异步计算的结果。实际应用中可以启动一个计算,将Future对象交给某个线程,然后执行其他操作。Future对象的所有者在结果计算好之后就可以获得它。Future接口具有下面的方法:

public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}

  第一个get方法的调用被阻塞,直到计算完成。如果在计算完成之前,第二个get方法的调用超时,抛出一个TimeoutException异常。如果运行该计算的线程被中断,两个方法都将抛出InterruptedException。如果计算已经完成,那么get方法立即返回。

  如果计算还在进行,isDone方法返回false;如果完成了,则返回true。

  可以用cancel方法取消该计算。如果计算还没有开始,它被取消且不再开始。如果计算处于运行之中,那么如果mayInterrupt参数为true,它就被中断。

  FutureTask包装器是一种非常便利的机制,同时实现了Future和Runnable接口。FutureTask有2个构造方法:

public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
} public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}

  通常,我们会使用Callable示例构造一个FutureTask对象,并将它提交给线程池进行处理,下面我们将展示这个内置的Future模式的使用。

public class RealData implements Callable<String> {
private String param;
public RealData(String param){
this.param = param;
}
@Override
public String call() throws Exception {
StringBuffer sb = new StringBuffer();
for(int i = 0 ; i< 10 ;i++){
sb.append(param);
try {
Thread.sleep(100);
}catch (InterruptedException e){ }
}
return sb.toString();
}
}

  上述代码实现了Callable接口,它的Call方法会构造我们需要的真实数据并返回,当然这个过程比较缓慢,这里使用Thread.sleep()来模拟它:

public class FutureMain {
public static void main(String[] args)
throws ExecutionException, InterruptedException {
//构造FutureTask
FutureTask<String> futureTask = new FutureTask<String>(new RealData("xxx"));
ExecutorService executorService = Executors.newFixedThreadPool(1);
//执行FutureTask,发送请求
//在这里开启线程进行RealData的call()执行
executorService.submit(futureTask); System.out.println("请求完毕。。。");
try {
//这里可以进行其他额外的操作,这里用sleep代替其他业务的处理
Thread.sleep(200);
}catch (InterruptedException e) {
e.printStackTrace();
}
//获取call()方法的返回值
//如果此时call()方法没有执行完成,则依然会等待
System.out.println("真实数据:"+futureTask.get());
}
}

  上述代码就是使用Future模式的典型。构造FutureTask时使用Callable接口,告诉FutureTask我们需要的数据应该有返回值。然后将FutureTask提交给线程池,接下来我们不用关心数据是怎么产生的,可以去做其他的业务逻辑处理,然后在需要的时候调用FutureTask.get()得到实际的数据。

Future设计模式的更多相关文章

  1. 并发模型之Future设计模式

    一.Futrue模式 客户端发送一个长时间的请求,服务端不需等待该数据处理完成便立即返回一个伪造的代理数据(相当于商品订单,不是商品本身),用户也无需等待,先去执行其他的若干操作后,再去调用服务器已经 ...

  2. 多线程设计模式 - Future模式之JAVA原生实现

    在之前一篇博客中介绍了Future设计模式的设计思想以及具体实现,今天我们来讲一下使用JDK原生的包如何实现. JDK内置的Future主要使用到了Callable接口和FutureTask类. Ca ...

  3. Java并发(8):CountDownLatch、CyclicBarrier、Semaphore、Callable、Future

    CountDownLatch.CyclicBarrier.Semaphore.Callable.Future  都位于java.util.concurrent包下,其中CountDownLatch.C ...

  4. Java多线程编程中Future模式的详解

    Java多线程编程中,常用的多线程设计模式包括:Future模式.Master-Worker模式.Guarded Suspeionsion模式.不变模式和生产者-消费者模式等.这篇文章主要讲述Futu ...

  5. java Future模式

    Java多线程编程中,常用的多线程设计模式包括:Future模式.Master-Worker模式.Guarded Suspeionsion模式.不变模式和生产者-消费者模式等.这篇文章主要讲述Futu ...

  6. Java多线程编程中Future模式的详解<转>

    Java多线程编程中,常用的多线程设计模式包括:Future模式.Master-Worker模式.Guarded Suspeionsion模式.不变模式和生产者-消费者模式等.这篇文章主要讲述Futu ...

  7. Java并发编程原理与实战三十一:Future&FutureTask 浅析

    一.Futrue模式有什么用?------>正所谓技术来源与生活,这里举个栗子.在家里,我们都有煮菜的经验.(如果没有的话,你们还怎样来泡女朋友呢?你懂得).现在女票要你煮四菜一汤,这汤是鸡汤, ...

  8. Future使用场景与分析

    前面分享了CountDownLatch的用法,但是由于分享过程中,发现有些朋友,问我Future与CountDownLatch的有什么区别? 答案:只是concurrent包下的并发帮助工具类,两者并 ...

  9. Python 并发编程:PoolExecutor 篇

    个人笔记,如有疏漏,还请指正. 使用多线程(threading)和多进程(multiprocessing)完成常规的并发需求,在启动的时候 start.join 等步骤不能省,复杂的需要还要用 1-2 ...

随机推荐

  1. file_get_contents是打工文件或URL获取内容的方法,比其稳定的还有curl_get_contents

    相信使用过file_get_contents函数的朋友都知道,当获取的$url访问不了时,会导致页面漫长的等待,甚至还能导致PHP进程占用CPU达100%,因此这个函数就诞生了 分享一个实际在用的函数 ...

  2. OpenGL 画出雷达动态扫描效果(一)

    最终效果如下所示 Demo下载  http://files.cnblogs.com/xd-jinjian/Debug.zip 源代码下载 http://download.csdn.net/detail ...

  3. vs2013编写的ASP.NET网站配置在XP IIS5.1上

     1.vs创建项目时,选择.Net Framework2.0 2.配置网站属性 Step1.进入命令行,注册ASP.NET IIS cmd => cd "C:\WINDOWS\Mi ...

  4. Oauth2.0(六):Resource Owner Password Credentials 授权和 Client Credentials 授权

    这两种简称 Password 方式和 Client 方式吧,都只适用于应用是受信任的场景.一个典型的例子是同一个企业内部的不同产品要使用本企业的 Oauth2.0 体系.在有些情况下,产品希望能够定制 ...

  5. opencv小问题大智慧

    opencv易错点记录 1. 判断条件不应使用CV_ASSERT(),而使用CV_Assert(). 2.  用下面这个自适应阈值必须进行数据的转换,不能直接传入数据!不然程序一直崩溃,找了很久才发现 ...

  6. [Delphi] 快速获取文件大小

    function GetFileSize(const fName: AnsiString): Int64; var hFile: THandle; begin hFile := _lopen(PAns ...

  7. Genymotion——VirtualBox cannot start virtual device

    提示"VirtualBox cannot start virtual device" 打开VirtualBox,想要在里面直接启动Genymotion模拟器,又出现错误,提示“Un ...

  8. 将文本转换为json的工具类

      JSONObject jsonObj  = JSONObject.fromObject("文本"); 参考:https://www.cnblogs.com/joahyau/p/ ...

  9. delphi XE7 判断手机返回键

    Using the Android Device's Back Button To make your application handle when users press the Back but ...

  10. z-index层级顺序 opacity透明度 display: none 模态框实现

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...