• 发消息和消费这俩操作之间是异步

用户通过app发来一个请求,会被转换成消息发往MQ,等MQ返回结果后,将其返回至app。

给MQ发消息的线程是处理请求的线程t1,但消费MQ结果的线程不是t1,那t1如何等待MQ的返回结果呢?

class Message{String id;String content;
}// 发消息
void send(Message msg){...
}// MQ消息返回后会调用该方法
// 执行该方法的线程 并非 发消息的线程
void onMessage(Message msg){...
}// 处理app的请求
Respond handle(){// 创建消息Message msg = new Message("1","{...}");// 发消息send(msg);// t1如何等待MQ返回的消息呢?String result = ...;
}

问题也就是异步转同步。

什么是Guarded Suspension模式?


比如离职前,约好了同事吃最后的晚餐

网上预订了希尔顿一个包厢,然后直接过去了,到那儿后大堂经理看眼包厢,发现服务员还在收拾,通知我们:“包厢服务员正在收拾,wait。”
过一会,大堂经理发现已经收拾完了,于是马上带我们去包厢就餐。

【等待收拾完】和【等待MQ返回消息】本质相同,都是等一个条件:

  • 就餐需要等待包间收拾完
  • 程序需要等待MQ返回消息

那现实里是如何解决这类问题的呢?

大堂经理很重要,顾客是否等待,完全由他协调。
程序也需要这样一个大堂经理,即设计模式:Guarded Suspension,“保护性地暂停”。

  • 【GuardedObject】:大堂经理

  • 【受保护对象】:包厢

  • 受保护对象的get():我们就餐

  • p:就餐的前提,即包厢收拾好了

  • 受保护对象的onChanged():服务员把包厢收拾好了,通过onChanged() fire 一个事件,该事件往往能改变p的计算结果。

  • 左侧线程:需要就餐的我们

  • 右侧线程:收拾包厢的服务员

Guarded Suspension模式结构图

GuardedObject的内部实现是管程的一个经典用法:

class GuardedObject<T>{//受保护的对象T obj;final Lock lock = new ReentrantLock();final Condition done =lock.newCondition();final int timeout=1;//获取受保护对象  T get(Predicate<T> p) {lock.lock();try {// MESA管程推荐写法while(!p.test(obj)){// 通过条件变量的await()方法实现等待done.await(timeout, TimeUnit.SECONDS);}}catch(InterruptedException e){throw new RuntimeException(e);}finally{lock.unlock();}//返回非空的受保护对象return obj;}//事件通知方法// 通过条件变量的signalAll()方法实现唤醒功能void onChanged(T obj) {lock.lock();try {this.obj = obj;done.signalAll();} finally {lock.unlock();}}
}

扩展Guarded Suspension模式

Guarded Suspension“大堂经理”能否解决小灰同学遇到的问题。

在处理Web请求的方法handleWebReq()中,可以调用GuardedObject#get()实现等待;在MQ消息的消费方法onMessage()中,可调用GuardedObject#onChanged()实现唤醒。

// 处理浏览器请求
Respond handleWebReq(){// 创建一消息Message msg1 = new Message("1","{...}");// 发送消息send(msg1);// 利用GuardedObject实现等待GuardedObject<Message> go = new GuardObjec<>();Message r = go.get(t->t != null);
}void onMessage(Message msg){// 如何找到匹配的go?GuardedObject<Message> go=???go.onChanged(msg);
}

但问题是,handleWebReq()里创建了GuardedObject对象的实例go,并调用get()等待结果,那在onMessage()方法中,如何才能够找到匹配的GuardedObject对象呢?
这类似服务员告诉大堂经理某某包间已经收拾好了,大堂经理如何根据包间找到就餐的人。现实世界里,大堂经理的头脑中,有包间和就餐人之间的关系图,所以服务员说完之后大堂经理立刻就能把就餐人找出来。

参考大堂经理识别就餐人的办法,来扩展一下Guarded Suspension模式,从而使它能够很方便地解决小灰同学的问题。在小灰的程序中,每个发送到MQ的消息,都有一个唯一性的属性id,所以我们可以维护一个MQ消息id和GuardedObject对象实例的关系,这个关系可以类比大堂经理大脑里维护的包间和就餐人的关系。

如何实现呢?下面代码是扩展Guarded Suspension,扩展后的

GuardedObject内部维护了一个Map:

  • Key是MQ消息id
  • Value是GuardedObject对象实例

静态方法:

  • create()
    创建一个GuardedObject对象实例,并根据key值将其加入到Map中
  • fireEvent()
    模拟的大堂经理根据包间找就餐人的逻辑。
class GuardedObject<T>{//受保护的对象T obj;final Lock lock = new ReentrantLock();final Condition done =lock.newCondition();final int timeout=2;//保存所有GuardedObjectfinal static Map<Object, GuardedObject> gos=new ConcurrentHashMap<>();//静态方法创建GuardedObjectstatic <K> GuardedObject create(K key){GuardedObject go=new GuardedObject();gos.put(key, go);return go;}static <K, T> void fireEvent(K key, T obj){GuardedObject go=gos.remove(key);if (go != null){go.onChanged(obj);}}//获取受保护对象  T get(Predicate<T> p) {lock.lock();try {//MESA管程推荐写法while(!p.test(obj)){done.await(timeout, TimeUnit.SECONDS);}}catch(InterruptedException e){throw new RuntimeException(e);}finally{lock.unlock();}//返回非空的受保护对象return obj;}//事件通知方法void onChanged(T obj) {lock.lock();try {this.obj = obj;done.signalAll();} finally {lock.unlock();}}
}

这样利用扩展后的GuardedObject来解决小灰同学的问题就很简单了,代码如下:

//处理浏览器发来的请求
Respond handleWebReq(){int id=序号生成器.get();//创建一消息Message msg1 = new Message(id,"{...}");//创建GuardedObject实例GuardedObject<Message> go=GuardedObject.create(id);  //发送消息send(msg1);//等待MQ消息Message r = go.get(t->t != null);
}
void onMessage(Message msg){//唤醒等待的线程GuardedObject.fireEvent(msg.id, msg);
}

总结

Guarded Suspension模式本质上是一种等待唤醒机制,只不过Guarded Suspension模式将其规范化了。
规范化好处是你无需重头思考如何实现,也无需担心实现程序的可理解性问题,同时也能避免一不小心写出个Bug来。但Guarded Suspension模式在解决实际问题的时候,往往还是需要扩展的,扩展的方式有很多,本篇文章就直接对GuardedObject的功能进行了增强,Dubbo中DefaultFuture这个类也是采用的这种方式。

Guarded Suspension模式也常被称作Guarded Wait模式、Spin Lock模式(因为使用了while循环去等待),这些名字都很形象,不过它还有一个更形象的非官方名字:多线程版本的if。单线程场景中,if语句是不需要等待的,因为在只有一个线程的条件下,如果这个线程被阻塞,那就没有其他活动线程了,这意味着if判断条件的结果也不会发生变化了。但是多线程场景中,等待就变得有意义了,这种场景下,if判断条件的结果是可能发生变化的。所以,用“多线程版本的if”来理解这个模式会更简单。

阿里P8级大佬详解并发编程里的设计模式之Guarded Suspension相关推荐

  1. 两个例子详解并发编程的可见性问题和有序性问题,通过volatile保证可见性和有序性以及volatile的底层原理——缓存一致性协议MESI和内存屏障禁止指令重排

    1. 并发编程的可见性问题 2. 并发编程的有序性问题 3. 使用volatile关键字解决可见性问题 4. 可见性问题的本质--缓存不一致 因为cpu执行速度很快,但是内存执行速度相对于CPU很慢, ...

  2. 01入门-ThreadLocal详解-并发编程(Java)

    文章目录 1 简介 2 基本使用 2.1 常用方法 2.2 小案例 3 ThreadLocal与Sycronized 4 应用场景 4.1 转账案例构建 4.2 问题 4.3 解决 5 后记 1 简介 ...

  3. 03弱引用内存泄露和hash冲突-ThreadLocal详解-并发编程(Java)

    文章目录 1 问题 2 内存泄露 3 弱引用 4 问题分析 4.1 key为强引用 4.2 key为弱引用 4.3 内存泄漏的真正原因 4.4 为什么Entry 的key使用弱引用 5 hash冲突的 ...

  4. 6年拉力工作经验,学了阿里P8级架构师的7+1+1落地项目,跳槽阿里年薪直接40W+

    前言 统一说明一下,楼主是研究生,一般的985毕业,之前在工作了6年,做过的大项目数不胜数, 比如再造淘宝项目落地实战,某滴网约车项目,多人在线即时对战网游服务器,和家云服务平台,前后端分离某喵微信商 ...

  5. 适合阿里P8级架构师提升学习的再造淘宝阿里团队代码落地实战项目

    前言 蓦然回首自己做开发已经十年了,这十年中我获得了很多,技术能力.培训.出国.大公司的经历,还有很多很好的朋友.但再仔细一想,这十年中我至少浪费了五年时间,这五年可以足够让自己成长为一个优秀的程序员 ...

  6. 全民大数据时代已来 阿里数加平台详解

    文章讲的是全民大数据时代已来 阿里数加平台详解,业界流行一种说法,云计算与大数据就是一枚硬币的两面,相生相惜,不可分割.在当下互联网时代,数据的价值越来越受到社会的认可,并在今天,已然成为一种普惠资源 ...

  7. 【阿里云课程】详解深度学习优化:泛化与正则化,学习率与最优化

    大家好,继续更新有三AI与阿里天池联合推出的深度学习系列课程,本次更新内容为第6课中两节,介绍如下: 第1节:泛化与正则化 第1节课内容为:泛化与正则化,讲述泛化的概念与重要性,各种正则化方法,包括显 ...

  8. [转]Hadoop集群_WordCount运行详解--MapReduce编程模型

    Hadoop集群_WordCount运行详解--MapReduce编程模型 下面这篇文章写得非常好,有利于初学mapreduce的入门 http://www.nosqldb.cn/1369099810 ...

  9. nps内网穿透服务器搭建教程(阿里云)-小宇特详解

    nps内网穿透服务器搭建教程(阿里云)-小宇特详解 前期准备 1.一台云服务器 2.配置安全组 3.在自己的电脑上安装nps 云服务器的话自己买,我这里使用的是阿里云 讲一下配置安全组 打开阿里云的官 ...

  10. 90分钟详解网络编程相关的细节处理丨 reactor丨网络io丨epoll丨C/C++丨Linux服务器开发丨后端开发丨Linux后台开发

    90分钟搞懂网络编程相关细节处理 1. 网络编程四要素 2. io多路复用 3. reactor三种基础封装方式 视频讲解如下,点击观看: 90分钟详解网络编程相关的细节处理丨 reactor丨网络i ...

最新文章

  1. 管理“95后”,切记这3点
  2. js不停地触发按钮的事件
  3. 如何进行职业生涯规划
  4. 为什么中国这么多高薪程序员,开发不出Java, Typescript, Python, Rust, Node.js这些基础设施?...
  5. js中的面向对象入门
  6. android开机动画多长时间_Android系统开机动画的一生
  7. 30个Oracle语句优化规则详解(1)
  8. filter-policy应用实验(华为设备)
  9. JavaScript基础用法
  10. gps测试软件用法,gps测量仪器使用方法及教程
  11. idea激活到2100年
  12. C++调用webservice服务生成客户端代码-gsoap
  13. 写一个获取非行间样式的函数
  14. 程序员必备技能之Markdown
  15. monkey--介绍
  16. javascript编写的网页小游戏,很给力
  17. 让预训练语言模型读懂数字:超对称技术联合复旦知识工场等发布10亿参数BigBang Transformer[乾元]金融大规模预训练语言模型
  18. 关于加密传输,镭速加密传输解决方案
  19. 高三女生因高中数学知识的数列解题技巧没掌握与梦想大学失之交臂
  20. 四神分析报告生成系统 1.6.1发布

热门文章

  1. c# 蓝牙虚拟串口_32feet.net 蓝牙虚拟串口编程
  2. [HTML+Bootstrap+CSS+jQuery] 时差计算器(计算时差、验证格式、当前时间、历史记录……)
  3. Pocket PC 模拟器上网设置
  4. ecshop模板支持php,让ecshop模板支持php运算
  5. xxx.so has text relocations. This is wasting memory and is a security risk. Please fix
  6. 用python将doc文件转换成docx文件
  7. 【历史上的今天】4 月 13 日:Damn Small Linux 首次发布;谷歌关闭短网址服务;数学先驱出生
  8. vue 白边 项目_GitHub - Hobby0/Vue-mmPlayer: 基于 Vue 的在线音乐播放器(PC) Online music player...
  9. Flutter Container设置 width 无效
  10. 怎么在计算机网络上添加文件,教你win7如何设置网络共享文件夹