Future设计模式
一、什么是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设计模式的更多相关文章
- 并发模型之Future设计模式
一.Futrue模式 客户端发送一个长时间的请求,服务端不需等待该数据处理完成便立即返回一个伪造的代理数据(相当于商品订单,不是商品本身),用户也无需等待,先去执行其他的若干操作后,再去调用服务器已经 ...
- 多线程设计模式 - Future模式之JAVA原生实现
在之前一篇博客中介绍了Future设计模式的设计思想以及具体实现,今天我们来讲一下使用JDK原生的包如何实现. JDK内置的Future主要使用到了Callable接口和FutureTask类. Ca ...
- Java并发(8):CountDownLatch、CyclicBarrier、Semaphore、Callable、Future
CountDownLatch.CyclicBarrier.Semaphore.Callable.Future 都位于java.util.concurrent包下,其中CountDownLatch.C ...
- Java多线程编程中Future模式的详解
Java多线程编程中,常用的多线程设计模式包括:Future模式.Master-Worker模式.Guarded Suspeionsion模式.不变模式和生产者-消费者模式等.这篇文章主要讲述Futu ...
- java Future模式
Java多线程编程中,常用的多线程设计模式包括:Future模式.Master-Worker模式.Guarded Suspeionsion模式.不变模式和生产者-消费者模式等.这篇文章主要讲述Futu ...
- Java多线程编程中Future模式的详解<转>
Java多线程编程中,常用的多线程设计模式包括:Future模式.Master-Worker模式.Guarded Suspeionsion模式.不变模式和生产者-消费者模式等.这篇文章主要讲述Futu ...
- Java并发编程原理与实战三十一:Future&FutureTask 浅析
一.Futrue模式有什么用?------>正所谓技术来源与生活,这里举个栗子.在家里,我们都有煮菜的经验.(如果没有的话,你们还怎样来泡女朋友呢?你懂得).现在女票要你煮四菜一汤,这汤是鸡汤, ...
- Future使用场景与分析
前面分享了CountDownLatch的用法,但是由于分享过程中,发现有些朋友,问我Future与CountDownLatch的有什么区别? 答案:只是concurrent包下的并发帮助工具类,两者并 ...
- Python 并发编程:PoolExecutor 篇
个人笔记,如有疏漏,还请指正. 使用多线程(threading)和多进程(multiprocessing)完成常规的并发需求,在启动的时候 start.join 等步骤不能省,复杂的需要还要用 1-2 ...
随机推荐
- Java 迭代器 工具类
迭代器:Iterator接口 //获取集合中的对象Iterator<E> iterator() interface Iterator { boolean hasNext(); Object ...
- sqoop操作之HIVE导出到ORACLE
示例数据准备 hive中创建dept表 create table dept( deptno int, dname string, loc string ) row format delimited f ...
- 生存分析与R
生存分析与R 2018年05月19日 19:55:06 走在码农路上的医学狗 阅读数:4399更多 个人分类: R语言 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blo ...
- uva-185-暴力枚举
请相信,这是一道水题,读了一周的题意 题意: 题目里面描述的那三个条件可以直接无视,关于罗马数字只要知道一个规则即可,映射如下 I 1 V 5X 10 L 50C 100 D 50 ...
- python入门-用户输入
1 input()函数来实现用户输入,程序在等待输入的时候会终止,获取用户的输入后继续 message = input("tell me something,and I will repre ...
- 尝试了一些时间,最简单的apache上设置用IP访问一个虚拟目录
就是其实新建一个 多域名访问的设置 <VirtualHost *:80> DocumentRoot /var/www/ ServerName IP</VirtualHost> ...
- 理解无偏估计(unbiased estimation)
判断一个估计量“好坏”,至少可以从以下三个方面来考虑: 无偏估计 有效性 一致性 参考内容: 如何理解无偏估计量?https://www.matongxue.com/madocs/808.html 衡 ...
- form表单提交参数封装
function getFormValues(element,options) { var data = {}; if(element == null || element == undefined) ...
- 数据库中查询json 样式的值的sql语句
参考:http://www.lnmp.cn/mysql-57-new-features-json.html 方式一: 可以查到json中的Key:value SELECT * FROM EDI.edi ...
- left join 如何增加where条件(在on的后面),这很重要
SELECT [学号], [姓名],[备注2],[年级],专业,[学院],[x30] FROM [总表] left join k指标体系 on 学号 = x01 where 年级='2014'