并发编程之future模式
一、Future模式简介
Future模式有点类似于网上购物,在你购买商品,订单生效之后,你可以去做自己的事情,等待商家通过快递给你送货上门。Future模式就是,当某一程序提交请求,期望得到一个答复。但是可能服务器程序对这个请求的处理比较慢,因此不可能马上收到答复。但是,在传统的单线程环境下,调用函数是同步的,它必须等到服务程序返回结果,才能继续进行其他处理。而Future模式下,调用方法是异步的,原本等待返回的时间段,在主调函数中,则可以处理其他的任务。传统的串行程序调用如下图所示:
Future模式的处理流程:
从图中可以看出,虽然call()本身是一个需要很长世间处理的程序。但是,服务程序不等数据处理完就立刻返回客户端一个伪数据(类似于商品订单,你购物需要的是商品本身),实现Future模式的客户端在拿到这个返回结果后,并不急于对它进行处理,而是去调用其它的业务逻辑,使call()方法有充分的时间去处理完成,这也是Future模式的精髓所在。在处理完其他业务逻辑后,最后再使用处理比较费时的Future数据。这个在处理过程中,就不存在无谓的等待,充分利用了时间,从而提升了系统的响应和性能。
二、Future模式的核心结构
下面以一个经典的Future实现为例,简单介绍下Future的核心实现。代码中Date接口:返回数据的接口;FutureDate类:实现Date接口,构造很快,返回一个虚拟的伪数据,需要装配RealDate;RealDate类:实现Date接口,返回真实数据,构造比较慢;Client:返回Date数据,立即返回FutureDate数据,并开启线程装配RealDate数据。
代码实现:
public interface Data {
public String getResult();
}
public class FutureData implements Data {
protected RealData realData = null;
protected boolean isReady = false;
//进行同步控制
public synchronized void setResult(RealData realData){
if(isReady){
return;
}
System.out.println("FutureData.setResult()");
this.realData=realData;
isReady = true;
notifyAll();
}
//实际调用返回RealDate的数据
@Override
public synchronized String getResult() {
while(!isReady){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("FutureData.getResult()");
return realData.result;
}
public class RealData implements Data{
protected final String result;
public RealData(String s) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 10; i++) {
sb.append(s);
try {
//模拟构造时间比较长
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
System.out.println("RealData.RealData()");
result = sb.toString();
}
public class Client {
public Data request(final String queryStr){
//返回伪数据
final FutureData futureData = new FutureData();
//开启线程构造真实数据
new Thread(){
public void run(){
RealData realData = new RealData(queryStr);
futureData.setResult(realData);
}
}.start();
//返回伪数据,等待真实数据加载
return futureData;
}
}
启动系统,调用Client发送请求:
public class TestMain {
public static void main(String[] args) {
Data data = new Client().request("123456");
System.out.println(data);
System.out.println(data.getResult());
}
}
可以看出,FutureDate是Future模式实现的关键,它实际是真实数据RealDate的代理,封装了获取RealDate的等待过程。
三、JDK内置实现
在JDK的内置并发包中,就已经内置了一种Future的实现,提供了更加丰富的线程控制,其基本用意和核心理念与上面实现代码一致。
在JDK中的Future模式中,最重要的是FutureTask类,它实现了Runnable接口,可以作为单独的线程运行。在其run()方法中,通过Sync内部类,调用Callable接口,并维护Callable接口的返回对象。当使用FutureTask.get()时,将返回Callable接口的返回对象。FutureTask还可以对任务本身进行其他控制操作。
利用Callable接口实现上述例子相同的操作:
RealDate类的实现:
public class Real1Data implements Callable<String>{
private String reaString;
public Real1Data(String reaString) {
super();
this.reaString = reaString;
}
@Override
public String call() throws Exception {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 10; i++) {
sb.append(reaString);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO: handle exception
}
}
return sb.toString();
}
}
Client代码实现:
public class Test1Main {
public static void main(String[] args) throws InterruptedException, ExecutionException {
FutureTask<String> future = new FutureTask<>(new Real1Data("1111"));
ExecutorService exe = Executors.newFixedThreadPool(1);
exe.submit(future);
System.out.println("FutureTask");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("FutureTask"+future.get());
}
}
可以看出RealDate的构造速度很快,其核心代码逻辑放在了call()中实现,不再需要Date和FutureDate,直接通过RealDate来构造FutureTask,将其作为单独的线程运行。在提交请求后,执行其他业务逻辑,做好通过FututeTask.get()方法,得到RealDate的执行结果。
Futute模式核心在于去除了主调用函数的等待时间,并使得原本需要等待的时间可以充分利用来处理其他业务逻辑,充分的利用了系统资源。
- 3410: [Usaco2009 Dec]Selfish Grazing 自私的食草者
- 3391: [Usaco2004 Dec]Tree Cutting网络破坏
- 3404: [Usaco2009 Open]Cow Digit Game又见数字游戏
- GIF/PNG/JPG和WEBP/base64/apng图片优点和缺点整理
- 3400: [Usaco2009 Mar]Cow Frisbee Team 奶牛沙盘队
- 3409: [Usaco2009 Oct]Barn Echoes 牛棚回声
- 浅谈webp
- 3385: [Usaco2004 Nov]Lake Counting 数池塘
- 2761: [JLOI2011]不重复数字(平衡树)
- 无图片字体icon
- 数据结构(三):栈与队列
- 3555: [Ctsc2014]企鹅QQ
- 3381: [Usaco2004 Open]Cave Cows 2 洞穴里的牛之二
- 3097: Hash Killer I
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Spring Boot 系列:日志动态配置详解
- Js电子时钟
- 函数的定义和调用
- this指向
- 严格模式
- 闭包与高阶函数
- IDEA设置maven修改settings.xml配置文件无法加载仓库
- 在Linux下如何根据域名自签发OpenSSL证书与常用证书转换 修改openssl.cnf配置文件创建根证书自签发泛域名证书将crt转pem格式生成 p12 格式的
- CAS 原子操作
- FlutterDojo设计之道—状态管理之路(七)
- Kubernetes K8S之存储ConfigMap详解 通过目录创建通过文件创建通过命令行创建通过yaml文件创建当前存在的ConfigMap使用ConfigMap
- Material Components——Shape的处理
- pandas系列 - (一)明细数据汇总简单场景应用
- Spring系列 SpringMVC的请求与数据响应
- PHP代码审计03之实例化任意对象漏洞