线程技术可以让我们的程序同时做多件事情,线程的工作模式有很多,常见的一种模式就是处理网站的并发,今天我来说说线程另一种很常见的模式,这个模式和前端里的ajax类似:浏览器一个主线程执行javascript,页面渲染等操作,当我们使用ajax向服务端发起请求,由于这个过程很慢,ajax的异步模式可以让我们无需一直等待服务端的响应,而在这个等待结果时间里做其他的事情,这个模式在线程技术力称之为Future模式。

  Future模式和我前面文章里说到的html5技术里的worker技术差不多,当我们一个程序执行流里某个操作特别耗时,我们可以另起一个线程专门执行这个繁琐耗时的任务,主线程则可以做其他的事情,下面是我自己找到的一个实现原生Future模式的代码,它主要参入者如下:

  TestMain.java:测试程序入口,主要是调用Client类,向Client发送请求;

  Client.java:返回Data对象,立即返回FutureData,并开启ClientThread线程装配RealData;

  Data.java:返回数据接口;

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

  RealData.java:真实数据,其构造是比较慢的。

  详细代码如下:

Data接口:

package cn.com.xSharp.futurePattern.simple;

/**
* 数据接口
* @author 俊
*
*/
public interface Data {
public String getData();
}

RealData代码:

package cn.com.xSharp.futurePattern.simple;

/**
* RealData是最终使用的数据,它构造很慢,因此用sleep来模拟
* @author 俊
* @since 2016-06-21 21:37
*/
public class RealData implements Data { protected final String result; public RealData(String param) {
StringBuffer sb = new StringBuffer();
for (int i = 0;i < 10;i++){
sb.append(param);
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
}
result = sb.toString();
} @Override
public String getData() {
return result;
} }

FutureData代码:

package cn.com.xSharp.futurePattern.simple;

public class FutureData implements Data {

	protected RealData realData = null;// FutureData对RealData的包装
protected boolean isReady = false; public synchronized void setRealData(RealData realData){
if (isReady){
return;
}
this.realData = realData;
isReady = true;
notifyAll();
} @Override
public synchronized String getData() {
while (!isReady){
try {
wait();
} catch (Exception e) {
e.printStackTrace();
}
}
return realData.result;
} }

Client代码:

package cn.com.xSharp.futurePattern.simple;

public class Client {

	public Data request(final String qryStr){
final FutureData futureData = new FutureData();
new Thread(){
public void run(){
RealData realData = new RealData(qryStr);
futureData.setRealData(realData);
}
}.start();
return futureData;
}
}

TestMain代码:

package cn.com.xSharp.futurePattern.simple;

public class TestMain {

	public static void main(String[] args) {
Client client = new Client();
Data data = client.request("xtq");
System.out.println("请求完毕!"); try {
for (int i = 0;i < 12;i++){
Thread.sleep(100);
System.out.println("可以做做其他的事情哦....");
} } catch (InterruptedException e) {
e.printStackTrace();
} System.out.println("数据==:" + data.getData());
} }

执行结果:

请求完毕!
可以做做其他的事情哦....
可以做做其他的事情哦....
可以做做其他的事情哦....
可以做做其他的事情哦....
可以做做其他的事情哦....
可以做做其他的事情哦....
可以做做其他的事情哦....
可以做做其他的事情哦....
可以做做其他的事情哦....
可以做做其他的事情哦....
可以做做其他的事情哦....
可以做做其他的事情哦....
数据==:xtqxtqxtqxtqxtqxtqxtqxtqxtqxtq

  JDK里在1.5之后提供了专门Future模式的实现,这里我使用FutureTask来实现Future模式。

  FutureTask在JDK文档里的解释:

  可取消的异步计算。利用开始和取消计算的方法、查询计算是否完成的方法和获取计算结果的方法,此类提供了对 Future 的基本实现。仅在计算完成时才能获取结果;如果计算尚未完成,则阻塞 get 方法。一旦计算完成,就不能再重新开始或取消计算。 可使用 FutureTask 包装 Callable 或 Runnable 对象。因为 FutureTask 实现了 Runnable,所以可将 FutureTask 提交给 Executor 执行。 除了作为一个独立的类外,此类还提供了 protected 功能,这在创建自定义任务类时可能很有用。

  下面是它的两个构造函数:

FutureTask(Callable<V> callable)
创建一个 FutureTask,一旦运行就执行给定的 Callable。
FutureTask(Runnable runnable, V result)
创建一个 FutureTask,一旦运行就执行给定的 Runnable,并安排成功完成时 get 返回给定的结果 。

  这里我首先使用第二个构造函数Runnable实现Future模式,代码如下:

package cn.com.futuretest;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask; public class FutureRunnable implements Runnable{
private Result result; // 操作的数据,模拟一个计算需要很长时间的数据 /* 初始化数据 */
public FutureRunnable(Result result) {
this.result = result;
} @Override
public void run() {
try {
for (int i = 0;i < 10;i++){
Thread.sleep(100);// 每隔100毫秒操作一次数据,模拟数据被长时间计算的场景
result.setData(result.getData() + ":" + "futureRunnable" + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
} public static void main(String[] args) {
Result r = new Result("xSharp");// 构造测试数据
FutureRunnable futureCallable = new FutureRunnable(r);// 初始化runnable
FutureTask<Result> task = new FutureTask<Result>(futureCallable, r);
// 构造固定大小为一个线程的线程池
ExecutorService executorService = Executors.newFixedThreadPool(1);
// 执行线程
executorService.execute(task);
System.out.println("执行完毕!"); try {
for (int i = 0;i < 15;i++){
Thread.sleep(100);
System.out.println("数据还在计算中等待中,你可以做别的事情" + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
} try {
System.out.println("打印结果是:" + task.get().getData());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}finally{
System.exit(0);
} } }

  执行结果:

执行完毕!
数据还在计算中等待中,你可以做别的事情0
数据还在计算中等待中,你可以做别的事情1
数据还在计算中等待中,你可以做别的事情2
数据还在计算中等待中,你可以做别的事情3
数据还在计算中等待中,你可以做别的事情4
数据还在计算中等待中,你可以做别的事情5
数据还在计算中等待中,你可以做别的事情6
数据还在计算中等待中,你可以做别的事情7
数据还在计算中等待中,你可以做别的事情8
数据还在计算中等待中,你可以做别的事情9
数据还在计算中等待中,你可以做别的事情10
数据还在计算中等待中,你可以做别的事情11
数据还在计算中等待中,你可以做别的事情12
数据还在计算中等待中,你可以做别的事情13
数据还在计算中等待中,你可以做别的事情14
打印结果是:xSharp:futureRunnable0:futureRunnable1:futureRunnable2:futureRunnable3:futureRunnable4:futureRunnable5:futureRunnable6:futureRunnable7:futureRunnable8:futureRunnable9

  接下来我使用Callable<V> 接口实现FutureTask,代码如下:

package cn.com.futuretest;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask; public class FutureCallable implements Callable<Result>{ private Result result; // 操作的数据,模拟一个计算需要很长时间的数据 /* 初始化数据 */
public FutureCallable(Result result) {
this.result = result;
} @Override
public Result call() throws Exception {
try {
for (int i = 0;i < 10;i++){
Thread.sleep(100);// 每隔100毫秒操作一次数据,模拟数据被长时间计算的场景
result.setData(result.getData() + ":" + "futureCallable" + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return result;
} public static void main(String[] args) {
long start = System.currentTimeMillis();
Result r = new Result("xSharp");// 构造测试数据
FutureCallable callable = new FutureCallable(r);
FutureTask<Result> task = new FutureTask<Result>(callable);
// 构造固定大小为一个线程的线程池
ExecutorService executorService = Executors.newFixedThreadPool(1);
// 执行线程
executorService.execute(task);
System.out.println("执行完毕!");
long curr01 = System.currentTimeMillis();
System.out.println("任务提交后的耗时:" + (curr01 - start) + "毫秒");
try {
for (int i = 0;i < 6;i++){
Thread.sleep(100);
System.out.println("数据还在计算中等待中,你可以做别的事情" + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
} try {
System.out.println("打印结果是:" + task.get().getData());
long end = System.currentTimeMillis();
System.out.println("总耗时:" + (end - start) + "毫秒");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}finally{
System.exit(0);
}
} }

  执行结果如下:

执行完毕!
任务提交后的耗时:6毫秒
数据还在计算中等待中,你可以做别的事情0
数据还在计算中等待中,你可以做别的事情1
数据还在计算中等待中,你可以做别的事情2
数据还在计算中等待中,你可以做别的事情3
数据还在计算中等待中,你可以做别的事情4
数据还在计算中等待中,你可以做别的事情5
打印结果是:xSharp:futureCallable0:futureCallable1:futureCallable2:futureCallable3:futureCallable4:futureCallable5:futureCallable6:futureCallable7:futureCallable8:futureCallable9
总耗时:1010毫秒

  这里我对代码做了一些调整,一个是加上了执行时间的统计,一个是我将干其他事情的程序执行时间变短,小于了线程本身执行的时间,这么做的目的是想和下面的程序对比,下面的代码当我执行线程后没有做其他的操作,而是直接获取线程执行的结果,具体代码如下:

package cn.com.futuretest;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask; public class NioFutureCallable implements Callable<Result> { private Result result; // 操作的数据,模拟一个计算需要很长时间的数据 /* 初始化数据 */
public NioFutureCallable(Result result) {
this.result = result;
} @Override
public Result call() throws Exception {
try {
for (int i = 0;i < 10;i++){
Thread.sleep(100);// 每隔100毫秒操作一次数据,模拟数据被长时间计算的场景
result.setData(result.getData() + ":" + "NioFutureCallable" + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return result;
} public static void main(String[] args) {
long start = System.currentTimeMillis();
Result r = new Result("xSharp");// 构造测试数据
NioFutureCallable callable = new NioFutureCallable(r);
FutureTask<Result> task = new FutureTask<Result>(callable);
// 构造固定大小为一个线程的线程池
ExecutorService executorService = Executors.newFixedThreadPool(1);
// 执行线程
executorService.execute(task);
System.out.println("执行完毕!");
long curr01 = System.currentTimeMillis();
System.out.println("任务提交后的耗时:" + (curr01 - start) + "毫秒"); /* 第一次获取返回数据 */
try {
System.out.println("第一次打印结果是:" + task.get().getData());
long curr02 = System.currentTimeMillis();
System.out.println("第一次获取结果耗时:" + (curr02 - start) + "毫秒");
} catch (InterruptedException e1) {
e1.printStackTrace();
} catch (ExecutionException e1) {
e1.printStackTrace();
} try {
for (int i = 0;i < 10;i++){
Thread.sleep(100);
System.out.println("数据还在计算中等待中,你可以做别的事情" + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
} try {
System.out.println("第二次打印结果是:" + task.get().getData());
long end = System.currentTimeMillis();
System.out.println("总耗时:" + (end - start) + "毫秒");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}finally{
System.exit(0);
} } }

  执行结果如下:

执行完毕!
任务提交后的耗时:7毫秒
第一次打印结果是:xSharp:NioFutureCallable0:NioFutureCallable1:NioFutureCallable2:NioFutureCallable3:NioFutureCallable4:NioFutureCallable5:NioFutureCallable6:NioFutureCallable7:NioFutureCallable8:NioFutureCallable9
第一次获取结果耗时:1009毫秒
数据还在计算中等待中,你可以做别的事情0
数据还在计算中等待中,你可以做别的事情1
数据还在计算中等待中,你可以做别的事情2
数据还在计算中等待中,你可以做别的事情3
数据还在计算中等待中,你可以做别的事情4
数据还在计算中等待中,你可以做别的事情5
数据还在计算中等待中,你可以做别的事情6
数据还在计算中等待中,你可以做别的事情7
数据还在计算中等待中,你可以做别的事情8
数据还在计算中等待中,你可以做别的事情9
第二次打印结果是:xSharp:NioFutureCallable0:NioFutureCallable1:NioFutureCallable2:NioFutureCallable3:NioFutureCallable4:NioFutureCallable5:NioFutureCallable6:NioFutureCallable7:NioFutureCallable8:NioFutureCallable9
总耗时:2012毫秒

  我们看到当我们直接获取结果时候,整个主线程都被阻塞了,直到结果返回后才会执行下面的后续操作,这也就是说如果计算还没结束,我们就想获取结果这样整个执行流程都将被阻塞,这点在我们合理使用Future模式时候很重要。

  除了使用FutureTask实现Future模式,我们还可以使用ExecutorService的submit方法直接返回Future对象,Future就和我前面设计的原生Future类似,当我们开始调用时候返回的是一个虚拟结果,其实实际的计算还没有结束,只有等待吗一会儿后结果才会真正的返回,代码如下:

package cn.com.futuretest;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future; public class RetFutureCallable implements Callable<Result>{ private Result result; // 操作的数据,模拟一个计算需要很长时间的数据 public RetFutureCallable() {
result = new Result("xSharp");
} @Override
public Result call() throws Exception {
try {
for (int i = 0;i < 10;i++){
Thread.sleep(100);// 每隔100毫秒操作一次数据,模拟数据被长时间计算的场景
result.setData(result.getData() + ":" + "RetFutureCallable" + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return result;
} public static void main(String[] args) {
long start = System.currentTimeMillis();
RetFutureCallable callable = new RetFutureCallable();
// 构造固定大小为一个线程的线程池
ExecutorService executorService = Executors.newFixedThreadPool(1);
// 执行线程
Future<Result> r = executorService.submit(callable);
System.out.println("执行完毕!");
long curr01 = System.currentTimeMillis();
System.out.println("任务提交后的耗时:" + (curr01 - start) + "毫秒");
try {
for (int i = 0;i < 6;i++){
Thread.sleep(100);
System.out.println("数据还在计算中等待中,你可以做别的事情" + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
} try {
System.out.println("打印结果是:" + r.get().getData());
long end = System.currentTimeMillis();
System.out.println("总耗时:" + (end - start) + "毫秒");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}finally{
System.exit(0);
}
} } 

  执行结果如下:

执行完毕!
任务提交后的耗时:5毫秒
数据还在计算中等待中,你可以做别的事情0
数据还在计算中等待中,你可以做别的事情1
数据还在计算中等待中,你可以做别的事情2
数据还在计算中等待中,你可以做别的事情3
数据还在计算中等待中,你可以做别的事情4
数据还在计算中等待中,你可以做别的事情5
打印结果是:xSharp:RetFutureCallable0:RetFutureCallable1:RetFutureCallable2:RetFutureCallable3:RetFutureCallable4:RetFutureCallable5:RetFutureCallable6:RetFutureCallable7:RetFutureCallable8:RetFutureCallable9
总耗时:1006毫秒

  好了,本文写完了。

线程笔记:Future模式的更多相关文章

  1. 线程技术 ☞ Future模式

    线程技术可以让我们的程序同时做多件事情,线程的工作模式有很多,常见的一种模式就是处理网站的并发,今天我来说说线程另一种很常见的模式,这个模式和前端里的ajax类似:浏览器一个主线程执行javascri ...

  2. Java中的Future模式原理自定义实现

    摘要:Future模式类似于js中的ajax等,是一个异步获取数据的机制,这里我把自己的一些形象理解通过代码实现了一下.该机制可以形象的理解为:调用获取数据的方法,首先获得一个没有装数据的空箱子(这个 ...

  3. Java线程池(Callable+Future模式)

    转: Java线程池(Callable+Future模式) Java线程池(Callable+Future模式) Java通过Executors提供四种线程池 1)newCachedThreadPoo ...

  4. java future模式 所线程实现异步调用(转载

    java future模式 所线程实现异步调用(转载) 在多线程交互的中2,经常有一个线程需要得到另个一线程的计算结果,我们常用的是Future异步模式来加以解决.Future顾名思意,有点像期货市场 ...

  5. Java是如何实现Future模式的?万字详解!

    JDK1.8源码分析项目(中文注释)Github地址: https://github.com/yuanmabiji/jdk1.8-sourcecode-blogs 1 Future是什么? 先举个例子 ...

  6. 架构师养成记--9.future模式讲解

    什么是future模式呢?解释这个概念之前我们先来了解一个场景吧,财务系统的结账功能,这个功能可能是每个月用一次,在这一个月中相关的数据量已经积累得非常大,这一个功能需要调用好几个存储过程来完成.假如 ...

  7. Future模式

    Future模式简介 Future模式有点类似于网上购物,在你购买商品,订单生效之后,你可以去做自己的事情,等待商家通过快递给你送货上门.Future模式就是,当某一程序提交请求,期望得到一个答复.但 ...

  8. 闲谈Future模式-订蛋糕

    一. Future模式简介 Future有道翻译:n. 未来:前途:期货:将来时.我觉得用期货来解释比较合适.举个实际生活中例子来说吧,今天我女朋友过生日,我去蛋糕店准备给女朋友定个大蛋糕,超级大的那 ...

  9. java Future 模式

    考慮這樣一個情況,使用者可能快速翻頁瀏覽文件中,而圖片檔案很大,如此在瀏覽到有圖片的頁數時,就會導致圖片的載入,因而造成使用者瀏覽文件時會有停頓 的現象,所以我們希望在文件開啟之後,仍有一個背景作業持 ...

随机推荐

  1. Fis3前端工程化之项目实战

    Fis3项目 项目目录结构: E:. │ .gitignore │ fis-conf.js │ index.html │ package.json │ README.md │ ├─material │ ...

  2. 对抗假人 —— 前后端结合的 WAF

    前言 之前介绍了一些前后端结合的中间人攻击方案.由于 Web 程序的特殊性,前端脚本的参与能大幅弥补后端的不足,从而达到传统难以实现的效果. 攻防本为一体,既然能用于攻击,类似的思路同样也可用于防御. ...

  3. Swift与C#的基础语法比较

    背景: 这两天不小心看了一下Swift的基础语法,感觉既然看了,还是写一下笔记,留个痕迹~ 总体而言,感觉Swift是一种前后端多种语言混合的产物~~~ 做为一名.NET阵营人士,少少多多总喜欢通过对 ...

  4. Jexus 5.8.2 正式发布为Asp.Net Core进入生产环境提供平台支持

    Jexus 是一款运行于 Linux 平台,以支持  ASP.NET.PHP 为特色的集高安全性和高性能为一体的 WEB 服务器和反向代理服务器.最新版 5.8.2 已经发布,有如下更新: 1,现在大 ...

  5. .NET Core中间件的注册和管道的构建(3) ---- 使用Map/MapWhen扩展方法

    .NET Core中间件的注册和管道的构建(3) ---- 使用Map/MapWhen扩展方法 0x00 为什么需要Map(MapWhen)扩展 如果业务逻辑比较简单的话,一条主管道就够了,确实用不到 ...

  6. WCF学习之旅—第三个示例之四(三十)

           上接WCF学习之旅—第三个示例之一(二十七)               WCF学习之旅—第三个示例之二(二十八)              WCF学习之旅—第三个示例之三(二十九)   ...

  7. Hyper-V 激活Windows系统重启后黑屏的解决方法 + 激活方法

    异常处理汇总-服 务 器 http://www.cnblogs.com/dunitian/p/4522983.html 服务器相关的知识点:http://www.cnblogs.com/dunitia ...

  8. Sublime的使用

    1.一个可扩展性强的编辑工具 2.如何安装扩展 通过View->Show Console菜单打开命令行. 按图操作: 在控制台输入,然后回车: import urllib.request,os; ...

  9. Android Ormlite 学习笔记1 -- 基础

    Ormlite 是一个开源Java数据实体映射框架.其中依赖2个核心类库: 1.ormlite-android-4.48.jar 2.ormlite-core-4.48.jar 新建项目,引用上面2个 ...

  10. Java 输出流中的flush方法

    转自:http://blog.csdn.net/jiyangsb/article/details/50984440 java中的IO流中的输出流一般都有flush这个操作,这个操作的作用是强制将缓存中 ...