14.Java中的Future模式
jdk1.7.0_79
本文实际上是对上文《13.ThreadPoolExecutor线程池之submit方法》的一个延续或者一个补充。在上文中提到的submit方法里出现了FutureTask,这不得不停止脚步将方向转向Java的Future模式。
Future是并发编程中的一种设计模式,对于多线程来说,线程A需要等待线程B的结果,它没必要一直等待B,可以先拿到一个未来的Future,等B有了结果后再取真实的结果。
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(callable); //主线程需要callable线程的结果,先拿到一个未来的Future
System.out.println(future.get()); //有了结果后再根据get方法取真实的结果,当然如果此时callable线程如果没有执行完get方法会阻塞执行完,如果执行完则直接返回结果或抛出异常
也就是说,Future它代表一个异步计算的结果。

上面就代表了Future模式的执行原理,根据网上的例子,我们可以来自己实现一个Future模式。
package com.future; /**
* 数据结果
* Created by yulinfeng on 6/18/17.
*/
public interface Data {
String getResult() throws InterruptedException;
}
package com.future; /**
* 结果的真实计算过程
* Created by yulinfeng on 6/18/17.
*/
public class RealData implements Data {
protected String data; public RealData(String data) {
try {
System.out.println("正在计算结果");
Thread.sleep(3000); //模拟计算
} catch (InterruptedException e) {
e.printStackTrace();
}
this.data = data + “ world”;
} public String getResult() throws InterruptedException {
return data;
}
}
package com.future; /**
* 真实结果RealData的代理
* Created by yulinfeng on 6/18/17.
*/
public class FutureData implements Data {
RealData realData = null; //对RealData的封装,代理了RealData
boolean isReady = false; //真实结果是否已经准备好 public synchronized void setResultData(RealData realData) {
if (isReady) {
return;
}
this.realData = realData;
isReady = true;
notifyAll(); //realData已经被注入到了futureData中,通知getResult方法
} public synchronized String getResult() throws InterruptedException {
if (!isReady) {
wait(); //数据还未计算好,阻塞等待
}
return realData.getResult();
}
}
package com.future; /**
* Client主要完成的功能包括:1. 返回一个FutureData;2.开启一个线程用于构造RealData
* Created by yulinfeng on 6/18/17.
*/
public class Client { public Data request(final String string) {
final FutureData futureData = new FutureData(); /*计算过程比较慢,单独放到一个线程中去*/
new Thread(new Runnable() { public void run() {
RealData realData = new RealData(string);
futureData.setResultData(realData);
}
}).start(); return futureData; //先返回一个“假”的futureData
}
}
/**
* 负责调用Client发起请求,并使用返回的数据。
* Created by yulinfeng on 6/18/17.
*/
public class Main {
public static void main(String[] args) throws InterruptedException {
Client client = new Client();
System.out.println("准备计算结果");
Data data = client.request("hello"); //立即返回一个“假”的futureData,可以不用阻塞的等待数据返回,转而执行其它任务
System.out.println("执行其它任务");
Thread.sleep(3000); //模拟执行其它任务
System.out.println("数据的计算结果为:" + data.getResult());
}
}
仔细阅读以上程序对Future模式的实现不难发现,Future模式是异步请求和代理模式的结合。当然在JDK中已经为我们实现好了Future模式。
修改RealData类:
package com.future; import java.util.concurrent.Callable; /**
* 结果的真实计算过程
* Created by yulinfeng on 6/18/17.
*/
public class RealData2 implements Callable<String> {
protected String data; public RealData2(String data) {
this.data = data;
}
public String call() throws Exception {
try {
System.out.println("正在计算结果");
Thread.sleep(2000); //模拟计算结果
} catch (InterruptedException e) {
e.printStackTrace();
}
this.data = data + " world";
return data;
}
}
修改Main测试类:
package com.future; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future; /**
* 负责调用Executor的submit,并使用返回的数据。
* Created by yulinfeng on 6/18/17.
*/
public class Main2 { public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService client = Executors.newSingleThreadExecutor(); //类似Client
System.out.println("准备计算结果");
Future<String> data = client.submit(new RealData2("hello")); //类似Client.request
System.out.println("执行其它任务");
Thread.sleep(3000);
System.out.println("数据的计算结果为:" + data.get());
}
}
现在回到上文还未解决完的AbstractExecutorService#submit方法。
类比上面的Client#request方法,在Client#request中先创建一个FutureData实例,而在AbstractExecutorService#submit中则是创建一个FutureTask实例,接着Client#request新创建一个线程用于异步执行任务,并直接返回FutureData,而在AbstractExecutorService#submit中同样也将任务交给了execute方法,并直接返回FutureTask。当然JDK中Future模式的实现更为复杂。
在《12.ThreadPoolExecutor线程池原理及其execute方法》中我们讲解了execute方法,在ThreadPoolExecutor$Worker#runWorker方法第1145行中是对task任务的调用:
//ThreadPoolExecutor$Worker#runWorker
task.run();
submit调用execute以执行run方法,实际执行的是FutureTask中的run方法。在FutureTask#run中,可以看到对任务Callable类型的task异步的执行,以及结果的保存。
14.Java中的Future模式的更多相关文章
- Java中的Future模式原理自定义实现
摘要:Future模式类似于js中的ajax等,是一个异步获取数据的机制,这里我把自己的一些形象理解通过代码实现了一下.该机制可以形象的理解为:调用获取数据的方法,首先获得一个没有装数据的空箱子(这个 ...
- (转)轻松学,Java 中的代理模式及动态代理
背景:讲到反射机制,肯定会想到动态代理. 轻松学,Java 中的代理模式及动态代理 代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强.值得注意的是,代理类和被代理类应该 ...
- Java中的Future相关
先上一个场景:假如你突然想做饭,但是没有厨具,也没有食材.网上购买厨具比较方便,食材去超市买更放心. 实现分析:在快递员送厨具的期间,我们肯定不会闲着,可以去超市买食材.所以,在主线程里面另起一个子线 ...
- java中 immutable,future,nio
什么是Future? 用过Java并发包的朋友或许对Future (interface) 已经比较熟悉了,其实Future 本身是一种被广泛运用的并发设计模式,可在很大程度上简化需要数据流同步的并发应 ...
- Java中的代理模式
代理模式在Java Web的框架中经常使用到.比如说在对数据库的访问中,核心功能是对数据库的增删改查,而连接数据库.处理事务等功能我们在开发中也要考虑到.所以我们将数据库的CRUD抽象到接口中,然后实 ...
- 说说Java中的代理模式
今天看到传智播客李勇老师的JDBC系列的第36节——通过代理模式来保持用户关闭连接的习惯.讲的我彻底蒙蔽了,由于第一次接触代理模式,感到理解很难,在博客园找到一篇文章,先记录如下: 引用自java设计 ...
- 谈谈Java中的代理模式
首先来看一下代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问.在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用, 其特征是代理类与 ...
- java中的dao模式
java中Dao模式 什么是DAO 1.Data Access Object(数据存取对象) 2.位于业务逻辑和持久化数据之间 3.实现对持久化数据的访问 DAO模式的作用 1隔离业务逻辑代码 ...
- Java中的单利模式
单利模式 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 这种模式涉及到一个单一的类,该类负责创 ...
随机推荐
- 内核初始化优化宏(__init, __devinit)
在内核里经常可以看到__init, __devinit这样的语句,这都是在init.h中定义的宏,gcc在编译时会将被修饰的内容放到这些宏所代表的section. 原文地址:http://blog.c ...
- 浅谈JavaScript时间与正则表达式
时间函数:var box = new Date() 函数 Demo: alert(Date.parse('4/12/2007')); //返回的是一个毫秒数11763 ...
- MySQL ProxySQL读写分离使用初探
目的 在美团点评DBProxy读写分离使用说明文章中已经说明了使用目的,本文介绍ProxySQL的使用方法以及和DBProxy的性能差异.具体的介绍可以看官网的相关说明,并且这个中间件也是percon ...
- js获取宽高
document.body.clientWidth ==> BODY对象宽度 document.body.clientHeight ==> BODY对象高度 document.docume ...
- python 基础安装使用
首先我们来学习一下怎么安装python和更新python,再来学习一些简单的解释器.变量.编码内容.循环等代码 第一步开始安装Python环境 安装Python windows: 1 2 3 4 5 ...
- 记MSSQL数据库sa账号短时间密码失效问题
在腾讯云服务器上安装了MSSQL2012,64位英文版本. sa账号的密码需要在一定时间内失效,无法访问.当时密码设置为admin@123. 最后修改了密码,改为比较复杂的密码,包含特殊字符,不包含a ...
- mysql source 报错 Unknown command '\'' 解决办法
系统:Windows2008 R2 source 导入数据总是报错. ERROR:Unknown command '\''.ERROR:Unknown command '\"'.ERROR ...
- 转:Redis 配置为 Service 系统服务
在Linux中,将程序配置为服务后,就可以使用service命令对系统服务进行管理,如:start(启动).stop(停止).restart(重启)等.Redis安装后默认不会配置为系统服务,本文将介 ...
- OpenStack修复影响宿主机的QEMU漏洞CVE-2017-2615
距离这个虚拟化层面的漏洞公告发出已有两个多月了,漏洞详情可以查看: 360安全应急响应中心-360发现QEMU严重漏洞 影响国内大部分公有云 简单来说是通过Cirrus VGA操作读取宿主机内存中的内 ...
- Java_中快速获取系统时间
直接调用System的currentTimeMillis()即可! long start = System.currentTimeMillis(); System.out.println(" ...