博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Future设计模式
阅读量:5078 次
发布时间:2019-06-12

本文共 6292 字,大约阅读时间需要 20 分钟。

一、什么是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
{ /** * 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对象的异步计算。

  Future保存异步计算的结果。实际应用中可以启动一个计算,将Future对象交给某个线程,然后执行其他操作。Future对象的所有者在结果计算好之后就可以获得它。Future接口具有下面的方法:

public interface Future
{ 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
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
{ 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
futureTask = new FutureTask
(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()得到实际的数据。

转载于:https://www.cnblogs.com/jing99/p/7332255.html

你可能感兴趣的文章
jQuery如何获得select选中的值?input单选radio选中的值
查看>>
设计模式 之 享元模式
查看>>
如何理解汉诺塔
查看>>
洛谷 P2089 烤鸡【DFS递归/10重枚举】
查看>>
15 FFT及其框图实现
查看>>
Linux基本操作
查看>>
osg ifc ifccolumn
查看>>
C++ STL partial_sort
查看>>
3.0.35 platform 设备资源和数据
查看>>
centos redis 安装过程,解决办法
查看>>
IOS小技巧整理
查看>>
WebDriverExtensionsByC#
查看>>
我眼中的技术地图
查看>>
lc 145. Binary Tree Postorder Traversal
查看>>
sublime 配置java运行环境
查看>>
在centos上开关tomcat
查看>>
重启rabbitmq服务
查看>>
正则表达式(进阶篇)
查看>>
无人值守安装linux系统
查看>>
【传道】中国首部淘宝卖家演讲公开课:农业本该如此
查看>>