一、什么是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. 补充 3:Golang 一些特性

    1 丰富的内置类型 2 函数多返回值 3 Go的错误处理 :   Go语言引入了3个关键字用于标准的错误处理流程,这3个关键字分别为defer. panic和 recover 4 在Go语言中,所有的 ...

  2. OpenGL chapter3 基础渲染

    3.1 基础图形管线 三种向OpenGl着色器传递渲染数据的方法:属性,Uniform和纹理.3.2 创建坐标系 3.2.1 正投影 GLFrustum::SetOrthographic(⋯⋯): 3 ...

  3. BPM与ESB

    BPM:业务流程管理  --监控处理流程的轨迹以及处理过程 开源:JBPM 场景: 1.单一系统的协同工作比如审批流程,请假流程 2.多个系统的集成,复用各个子系统,构建新的处理流程(流程的优化与流程 ...

  4. StrokesPlus发送快捷键命令列表

    StrokesPlus重度使用者! @ = Windows按键 + = SHIFT按键 ^ = CTRL按键 % = ALT按键 {ADD} = VK_ADD {APPS} = VK_APPS {AT ...

  5. RAID5---块,条带,校验,旋转,同步/异

    冗余(奇偶校验)块:RAID5中在同一个条带中用一个块来存放冗余信息,冗余信息示其他块的"异或"值,这样在同一条带中就只有(n-1)个块是实际的数据,所以RAID5中阵列容量是(n ...

  6. web service初探

    概述:Web service是一个平台独立.低耦合的.自包含的.基于可编程的web的应用程序,可使用开放的XML(标准通用标记语言下的一个子集)标准来描述.发布.发现.协调和配置这些应用程序,用于开发 ...

  7. QT中使用自己定的类和Vector出现错误

    关于QT自定义类不能调用问题: 在Main()函数里面一定要定义include“XXX.cpp”include“XXX.h”,具体原因我也不知道为什么这样定义,是在一个贴吧看见的,弄了好久才解决. 第 ...

  8. Sklearn数据集与机器学习

    sklearn数据集与机器学习组成 机器学习组成:模型.策略.优化 <统计机器学习>中指出:机器学习=模型+策略+算法.其实机器学习可以表示为:Learning= Representati ...

  9. debian新增加用户 拥有ROOT权限

    方案一 : 已经有了 新增加的用户  但是没有ROOT 权限: 首需要切换到root身份$su -(注意有- ,这和su是不同的,在用命令"su"的时候只是切换到root,但没有把 ...

  10. Java虚拟机汇编代码

    0:将一个常量加载到操作数栈 3:数值从操作数栈存储到局部变量表 4:将int类型的常量加载到操作数栈 5:数值从操作数栈存储到局部变量表 6:将一个局部变量加载到操作栈 7:将一个局部变量加载到操作 ...