专栏首页架构师进阶并发模式(一)Future模式 顶

并发模式(一)Future模式 顶

并行设计模式是对一些常用的多线程结构的总结和抽象,与串行程序设计相比,并行程序更复杂。

前言

常用的并发设计模式有Future模式、Master-Worker模式、Guarded Suspension模式、不变模式、生产者-消费者模式,在多线程环境中,合理使用模式,可以提高程序性能,优化程序设计。

接下来会记录这些模式的学习过程,一一成文,以便以后查阅和复习。

不变模式

不变模式的实现很简单,这里说明一下。不变模式天生就是多线程友好的。一个对象一旦被创建,则它的内部状态将永远不会发生改变,没有一个线程可以修改其内部状态和数据,同时内部状态也不会自行发生改变。基于这些特性,不变模式的对象,在多线程环境中不需要同步控制。

主要使用场景:

  1. 当对象创建后,其内部状态和数据不再发生任何变化;
  2. 对象需要被共享、被多线程频繁访问。

代码实现:

  1. final修饰类,确保不能被继承,没有子类;
  2. 所有属性私有化,并用final标记,确保不会被修改;
  3. 移除setter方法和其他所有修改自身属性的方法;
  4. 构造函数的参数包括所有的属性,提供赋值的窗口。

在JDK中,所有基本类型的包装类、String都是使用不变模式实现的。

Future模式

概念

Future模式是多线程设计常用的一种设计模式,类似商品订单。商品下单后,会立即得到下单成功的通知,客户不用等待后续商家的操作,只等配送到家即可,下单后到收到商品这段时间,客户可以做其他事情,不用在家等着商品。Future模式也类似Ajax的异步请求,不用等待处理结果。

处理流程

传统处理流程

客户端发出call请求,这个请求需要很长一段时间才能返回。客户端一直等待着,直到数据返回,随后进行其他业务处理。

Future模式流程

服务程序不需要等待数据处理完成便立即返回客户端伪造的数据(相当于商品的订单,而不是实际商品),客户端拿到这个返回结果后,并不急于对其处理,而是利用等待的时间,调用其他业务逻辑。这就是Future模式的核心所在。

主要参与者

Main:系统启动,调用client发出请求。 Client:返回Data对象,立即返回FutureData,并开启ClientThread线程装配RealData。 Data:返回数据的接口。 FutureData:Future数据,构造很快,但是是一个虚拟的数据,需要装配RealData。 RealData:真实数据,其构造过程是比较慢的。

代码实现

Future模式结构图

Main

//Future的简单实现。
// 系统启动,调用client发出请求
public class Main {
    public static void main(String[] args) {
        Client client = new Client();
        //这里会立即返回,因为得到的是FutureData,而不是RealData
        Data data = client.request("name");
        System.out.println("发送请求完毕。。。");
        try {
            //模拟其他业务逻辑处理
            //处理过程中,RealData被创建,充分利用了等待的时间。
            System.out.println("Main 正在调用其他业务逻辑。。。");
            Thread.sleep(1000);
            System.out.println("Main 其他业务处理完成。。。");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //使用真实的数据
        System.out.println("数据= " + data.getResult());
    }
}

Client

//返回Data对象,立即返回FutureData,并开启ClientThread线程装配RealData
public class Client {
    public Data request(final String requestStr){
        final FutureData future=new FutureData();
        //RealData构建过程很慢,所以在单独的线程中进行
        new Thread(){
            @Override
            public void run() {
                RealData realData=new RealData(requestStr);
                future.setRealData(realData);
            }
        }.start();
        return future;//future会被立即返回
    }
}

Data

//返回数据的接口
public interface Data {
    public String getResult();
}

FutureData

//Future数据,构造很快,但是是一个虚拟的数据,需要装配RealData
//FutureData是Future模式的核心,是RealData的真实代理,封装了等待RealData的过程。
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();//RealData已被注入,通知getResult()
    }


    @Override
    public synchronized String getResult() {
        //等待RealData构造完成
        while (!isReady){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return realData.result;//由RealData实现
    }
}

RealData

//真是数据,其构造过程是比较慢的
public class RealData implements Data {
    protected final String result;
    public RealData(String para){
        StringBuffer buffer=new StringBuffer();
        System.out.println("RealData 正在构造真实数据。。。");
        for (int i=0;i<10;i++){
            buffer.append(para);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
       System.out.println("RealData 真实数据构造完成。。。");
        result=buffer.toString();
    }
    @Override
    public String getResult() {
        return result;
    }
}

FutureData其实是RealData的一个代理,实现RealData的延迟效果。

JDK的内置实现

JDK内置的Future模式

代码优化

使用JDK内置的Future模式优化代码,首先要实现Callable这个接口,DataFutureDataClient对象就不需要了。

RealData

public class RealData implements Callable<String> {
    private String para;

    public RealData(String para) {
        this.para = para;
    }

    //call中写具体的业务逻辑
    @Override
    public String call() throws Exception {
        //模拟真实业务逻辑,执行很慢
        StringBuffer buffer=new StringBuffer();
        System.out.println("RealData 正在构造真实数据。。。");
        for (int i=0;i<10;i++){
            buffer.append(para);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("RealData 真实数据构造完成。。。");
        return buffer.toString();
    }
}

Main

在Main方法中,直接通过RealData构造FutureTask,作为单独的线程运行。

public class Main {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //构造FutureTask
        FutureTask<String> future=new FutureTask<String>(new RealData("name"));
        ExecutorService executor= Executors.newFixedThreadPool(1);
        //执行FutureTask,相当于client.request()发送请求
        //开启线程进行RealData的call()执行
        executor.submit(future);
        System.out.println("发送请求完毕");

        try {
            System.out.println("Main 正在调用其他业务逻辑。。。");
            Thread.sleep(2000);
            System.out.println("Main 其他业务处理完成。。。");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //使用真实的数据
        //相当于data.getResult(),取得call()方法的返回值
        //如果call()方法没有执行完成,则依然会等待
        System.out.println("数据= " + future.get());
    }
}

Future模式的核心在于除了主函数中的等待时间,并使得原本需要等待的时间可以用于其他业务逻辑的处理,充分利用了计算机资源,提高系统性能。

参考资料

葛一鸣:Java程序性能优化-让你的Java程序更快、更稳定

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 基础服务系列-git仓库删除所有提交历史记录,成为一个干净的新仓库

    需要更换代码Git仓库,想把之前的commits全部删掉。网上找了众多方法,都不起作用。踩了2次坑,幸好查到了。

    用户2146693
  • 基础服务系列-centos7 安装tomcat7

    <Context path="/" docBase="/usr/local/apache-tomcat-7.0.88/webapps" reloadable="...

    用户2146693
  • Spring Boot集成CKEditor 顶

    项目中需要用到富文本编辑器,朋友推荐用CKEditor。CKEditor可以和Spring mvc很好的集成。CKEditor与CKFinder学习–整合Spr...

    用户2146693
  • 处理Empty Mono的方法

    在Reactor编程中有时候我们需要对empty Mono<T>做一些特定业务逻辑。下面看一段非reactor编程的代码:

    颇忒脱
  • [译] 提高效率的 10 个 Git 小技巧

    你是否有过破坏了某个文件,想要重新开始? 或者需要的文件在另一个分支中? 下面的命令让你直接从某个分支取到那个文件。

    savokiss
  • [Go 语言社区] Golang架构底层---日志函数

    服务器后台架构,日志是必不可少的一个功能模块,日志可以分为很多中:统计日志,访问日志,错误日志等 今天大家发是运行中的日志函数 // 日志函数,传入数据为字...

    李海彬
  • 图解Java设计模式之原型模式

    现在有一只羊tom,姓名为 : tom,年龄为 :1,颜色为 :白色,请编写程序创建和tom羊属性完全相同的10只羊。

    海仔
  • 注重用户并不意味着为每一个潜在的客户服务 | 海外*风投

    作者:T 客汇 张苏月,原文作者 Michael Seibel 关键词:用户,产品,初创公司 网址:www.tikehui.com ? 创业之初,许多...

    人称T客
  • C# 温故而知新:Stream篇(三)

    TextWriter 和 StreamWriter 目录: 为何介绍TextWriter? TextWriter的构造,常用属性和方法 IFormatProvi...

    逸鹏
  • Spring参数错误时输出Http内容

    Spring应用中,一般使用Aop进行请求参数和返回值的日志输出,但是这里有一个前提,就是用户请求的参数一定好可以转换为方法参数,但是经常前端请求是一个错误的J...

    十毛

扫码关注云+社区

领取腾讯云代金券