Java 并发编程实战
王宝令
资深架构师
72486 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 51 讲
学习攻略 (1讲)
Java 并发编程实战
15
15
1.0x
00:00/00:00
登录|注册

31 | Guarded Suspension模式:等待唤醒机制的规范实现

fireEvent()
create()
gos
onChanged(T obj)
get(Predicate p)
受保护的对象
使用sleep()方法的实现
其他名字
扩展方式
Guarded Suspension模式的好处
GuardedObject扩展
GuardedObject
Guarded Suspension模式
小灰遇到的问题
课后思考
总结
扩展Guarded Suspension模式
Guarded Suspension模式结构图
介绍
Guarded Suspension模式

该思维导图由 AI 生成,仅供参考

前不久,同事小灰工作中遇到一个问题,他开发了一个 Web 项目:Web 版的文件浏览器,通过它用户可以在浏览器里查看服务器上的目录和文件。这个项目依赖运维部门提供的文件浏览服务,而这个文件浏览服务只支持消息队列(MQ)方式接入。消息队列在互联网大厂中用的非常多,主要用作流量削峰和系统解耦。在这种接入方式中,发送消息和消费结果这两个操作之间是异步的,你可以参考下面的示意图来理解。
消息队列(MQ)示意图
在小灰的这个 Web 项目中,用户通过浏览器发过来一个请求,会被转换成一个异步消息发送给 MQ,等 MQ 返回结果后,再将这个结果返回至浏览器。小灰同学的问题是:给 MQ 发送消息的线程是处理 Web 请求的线程 T1,但消费 MQ 结果的线程并不是线程 T1,那线程 T1 如何等待 MQ 的返回结果呢?为了便于你理解这个场景,我将其代码化了,示例代码如下。
class Message{
String id;
String content;
}
//该方法可以发送消息
void send(Message msg){
//省略相关代码
}
//MQ消息返回后会调用该方法
//该方法的执行线程不同于
//发送消息的线程
void onMessage(Message msg){
//省略相关代码
}
//处理浏览器发来的请求
Respond handleWebReq(){
//创建一消息
Message msg1 = new
Message("1","{...}");
//发送消息
send(msg1);
//如何等待MQ返回的消息呢?
String result = ...;
}
看到这里,相信你一定有点似曾相识的感觉,这不就是前面我们在《15 | Lock 和 Condition(下):Dubbo 如何用管程实现异步转同步?》中曾介绍过的异步转同步问题吗?仔细分析,的确是这样,不过在那一篇文章中我们只是介绍了最终方案,让你知其然,但是并没有介绍这个方案是如何设计出来的,今天咱们再仔细聊聊这个问题,让你知其所以然,遇到类似问题也能自己设计出方案来。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Guarded Suspension模式是一种规范实现的等待唤醒机制,旨在解决异步操作中的等待唤醒问题。文章以Web项目中等待消息队列(MQ)返回结果的场景为例,引入Guarded Suspension模式,类比大堂经理协调就餐的情景,通过GuardedObject对象的get()和onChanged()方法实现了等待和唤醒的功能。进一步扩展了Guarded Suspension模式,引入Map来维护MQ消息id和GuardedObject对象实例的关系,更方便地解决了等待MQ返回消息的问题。通过示例代码展示了如何利用扩展后的GuardedObject来解决实际问题。总的来说,Guarded Suspension模式提供了一种规范的解决方案,能够有效地解决异步操作中的等待唤醒问题。 Guarded Suspension模式本质上是一种等待唤醒机制的规范实现,规范化的好处在于无需重头思考如何实现,也能避免一不小心写出Bug。在解决实际问题时,往往需要对其进行扩展,可以对GuardedObject的功能进行增强,或创建新的类来实现扩展。Guarded Suspension模式也常被称作Guarded Wait模式、Spin Lock模式,甚至有一个更形象的非官方名字:多线程版本的if。文章还引发了关于使用done.await()加锁和直接使用sleep()方法的讨论,探讨了不同实现方式的优劣。 文章内容涵盖了Guarded Suspension模式的基本原理、扩展方法以及实际应用,对读者快速了解该模式具有重要意义。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Java 并发编程实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(66)

  • 最新
  • 精选
  • 一道阳光
    当从消息队列接收消息失败时,while循环会一直执行下去,永远不会结束,回占用大量资源。

    作者回复: 👍

    2019-05-09
    10
    63
  • Felix Envy
    老师,感觉如果有方法调用了GuardedObect.create方法但是没有任何其他线程调用fireEvent方法会造成内存泄漏啊,这种情况需要考虑吗?

    作者回复: 👍 需要,等待超时后要把他移除。

    2019-05-12
    2
    49
  • zhangwei
    老师,我有个疑问,希望帮忙解答。如果Web应用是集群的,A节点处理HTTP请求后发了MQ,B节点的onMessage消费了回执消息,那么A节点怎么把结果响应给客户端呢?疑问好久了,希望老师给个思路,谢谢!

    作者回复: 我了解有人是这么做的:把回执消息放到redis的list中,按照ip重新分组之后从redis中再次消费。 也可以按照ip建立不同的topic。

    2019-05-18
    14
    40
  • Mr.Brooks
    没有锁也无法保证内存可见性吧

    作者回复: 👍

    2019-05-10
    2
    25
  • 朵朵集团总裁
    如果mq服务挂了无法消费,会引起web请求服务很多线程出于等待状态,是不是应该whlie循环加上超时。

    作者回复: 总裁您说的对👍

    2020-04-01
    10
  • 张三
    接入微信支付支付宝支付里边,也需要提供一个回调函数,onChange()就是一个回调函数吧,不过微信支付宝支付是异步回调,是不是也可以改成这种?微信支付宝里边的其它第三方支付是不是就是这种模式,因为支付成功之后跳转到它们自己的页面,而不是微信支付宝官方的支付成功界面

    作者回复: 这个回调函数和mq的回调函数从服务接入方的角度看是一样的

    2019-05-10
    9
  • 飞翔
    老师 future.get 就是guarded suspension 的应用吧

    作者回复: 是的👍

    2019-09-24
    3
    7
  • 君哥聊技术
    如果以文中的最后一段示例代码来看,每一个请求生成一个id,对应一个GuardedObject,并没有线程安全问题。我觉得可以去掉锁。 但是加sleep的话,没有办法唤醒,只能等到超时。

    作者回复: await和notify获取锁才能调用,所以不能去掉锁

    2019-05-09
    7
  • 庄墨寒
    老师, 我觉得您只是举个例子吧. 真实的生成环境, A和B肯定都是一个集群; A 给 B发一个消息. B处理完后再给A发一个消息, 在A 集群中发送和接收消息的大概率两台不同的机器. 解决这个问题两种办法: 1. web 请求长轮询; 2. A集群有分布式的缓存, A的某台机器处理消息后把结果写到缓存, 处理web请求的机器有专门的线程去轮询.

    作者回复: 这是个真实的例子,集群中有两台机器,a->b有一个参数topic,b->a的时候根据传入的topic参数来确定写入哪个topic的,两台机器的topic参数不同,所以发送和接收是能对应上的

    2019-09-06
    6
  • ipofss
    老师,这节听了个大概,不是非常懂。其中有一点没理解,get方法加锁后,while判断一直都为true,也就一直不会释放锁,那onChanged方法进去之后,获取不到锁,双方不久互相死等下去了么,我应该还是哪里没想明白

    作者回复: wait会释放锁,建议重看第一部分管程相关的内容

    2019-10-30
    5
收起评论
显示
设置
留言
66
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部