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

15 | Lock和Condition(下):Dubbo如何用管程实现异步转同步?

doReceived()
get()
DefaultFuture类
Dubbo的实现
TCP协议的异步特性
方法实现时创建新线程
调用方创建子线程
调用方是否需要等待结果
signalAll()
signal()
await()
队列不满
队列不空
Lock&Condition实现的管程支持多个条件变量
Java语言内置的管程只有一个条件变量
非阻塞地获取锁
支持超时
能够响应中断
DefaultFuture里唤醒等待的线程使用signal()的合理性
DefaultFuture的代码
原理与实现
理解管程模型
理解Lock&Condition
异步转同步
实现方式
区别
与synchronized实现的管程的区别
线程等待和通知
例子:阻塞队列
区别
特性
课后思考
总结
Dubbo源码分析
同步与异步
Lock&Condition实现的管程
Condition实现了管程模型里面的条件变量
Java SDK并发包里的Lock
Lock&Condition(下):Dubbo如何用管程实现异步转同步?
参考文章

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

在上一篇文章中,我们讲到 Java SDK 并发包里的 Lock 有别于 synchronized 隐式锁的三个特性:能够响应中断、支持超时和非阻塞地获取锁。那今天我们接着再来详细聊聊 Java SDK 并发包里的 Condition,Condition 实现了管程模型里面的条件变量
《08 | 管程:并发编程的万能钥匙》里我们提到过 Java 语言内置的管程里只有一个条件变量,而 Lock&Condition 实现的管程是支持多个条件变量的,这是二者的一个重要区别。
在很多并发场景下,支持多个条件变量能够让我们的并发程序可读性更好,实现起来也更容易。例如,实现一个阻塞队列,就需要两个条件变量。
那如何利用两个条件变量快速实现阻塞队列呢?
一个阻塞队列,需要两个条件变量,一个是队列不空(空队列不允许出队),另一个是队列不满(队列已满不允许入队),这个例子我们前面在介绍管程的时候详细说过,这里就不再赘述。相关的代码,我这里重新列了出来,你可以温故知新一下。
public class BlockedQueue<T>{
final Lock lock =
new ReentrantLock();
// 条件变量:队列不满
final Condition notFull =
lock.newCondition();
// 条件变量:队列不空
final Condition notEmpty =
lock.newCondition();
// 入队
void enq(T x) {
lock.lock();
try {
while (队列已满){
// 等待队列不满
notFull.await();
}
// 省略入队操作...
//入队后,通知可出队
notEmpty.signal();
}finally {
lock.unlock();
}
}
// 出队
void deq(){
lock.lock();
try {
while (队列已空){
// 等待队列不空
notEmpty.await();
}
// 省略出队操作...
//出队后,通知可入队
notFull.signal();
}finally {
lock.unlock();
}
}
}
不过,这里你需要注意,Lock 和 Condition 实现的管程,线程等待和通知需要调用 await()、signal()、signalAll(),它们的语义和 wait()、notify()、notifyAll() 是相同的。但是不一样的是,Lock&Condition 实现的管程里只能使用前面的 await()、signal()、signalAll(),而后面的 wait()、notify()、notifyAll() 只有在 synchronized 实现的管程里才能使用。如果一不小心在 Lock&Condition 实现的管程里调用了 wait()、notify()、notifyAll(),那程序可就彻底玩儿完了。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Dubbo中异步转同步的实现原理是通过使用管程模型中的Lock和Condition来实现的。文章首先介绍了Java SDK并发包中的Lock和Condition,以及它们相对于synchronized的灵活性和丰富功能。通过详细讲解阻塞队列的实现,强调了Lock和Condition的特点和使用方法。接着,文章引入了同步与异步的概念,并解释了Dubbo中异步转同步的原理。通过分析Dubbo源码,重点讲解了DefaultFuture类的实现,展示了其使用Lock和Condition实现的管程模型来实现异步转同步的功能。最后,总结了Lock和Condition的灵活性和功能丰富性,强调了理解原理对于快速学好并发编程的重要性。通过实例和源码分析,使读者能够快速了解并掌握Lock和Condition的使用方法以及Dubbo中异步转同步的实现原理。文章内容深入浅出,对于想要深入理解并发编程的读者具有很高的参考价值。

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

全部留言(104)

  • 最新
  • 精选
  • ZOU志伟
    不合理,会导致很多请求超时,看了源码是调用signalAll()

    作者回复: 写这一章的时候还是signal,后来有人提了个bug,就改成signalall了

    2019-04-03
    20
    159
  • 张天屹
    我理解异步的本质是利用多线程提升性能,异步一定是基于一个新开的线程,从调用线程来看是异步的,但是从新开的那个线程来看,正是同步(等待)的,只是对于调用方而言这种同步是透明的。正所谓生活哪有什么岁月静好,只是有人替你负重前行。

    作者回复: 总结的太有文采了!异步加上非阻塞IO才有威力

    2019-04-04
    13
    110
  • 右耳听海
    in the method of org.apache.dubbo.remoting.exchange.support.DefaultFuture#doReceived, I think we should call done.signalAll() instead of done.signal() ,and it's unnecessary to check done != null because it's always true

    作者回复: 留言这两点有同学都提到了。我表示震撼!

    2019-04-28
    50
  • Geek_e6f3ec
    老师关于dubbo源码的执行流程有一点疑问。 以下是源码 // 调用通过该方法等待结果 Object get(int timeout){ long start = System.nanoTime(); lock.lock(); try{ while (!isDone()){ done.wait(timeout); // 在这里调用了等待方法后面的代码还能执行吗? 我理解的管程,是在条件变量等待队列中阻塞等待,被唤醒之后也不是马上执行也要去管程入口等待队列,也就是lock.lock处等待获取锁。 老师是这样的吗? long cur = System.nanoTime(); if (isDone()||cur-start> timeout){ break; } } }finally { lock.unlock(); } return returnFromResponse(); }

    作者回复: 会去获取锁,但是获取锁后,会执行wait后的代码

    2019-05-15
    4
    20
  • 木刻
    老师今天提到异步转同步,让我想到这两天看的zookeeper客户端源码,感觉应该也是这个机制,客户端同步模式下发送请求后会执行packet.wait,收到服务端响应后执行packet.notifyAll

    作者回复: 👍

    2019-04-02
    17
  • 苏格拉底23
    老师您好! 有一个基本的问题不明白,如果每个request对应一个线程,似乎并没有用到共享的资源,那么为什么要加锁呢?

    作者回复: 这里只是利用管程实现线程的阻塞和唤醒

    2019-06-23
    2
    14
  • ban
    老师,求指教 DefaultFuturewhile这个类为什么要加 while(!isDone()) 这个条件,我看代码while里面加了done.await(timeout);是支持超时的,就是说设置5秒超时, if (isDone() || cur-start > timeout){,只要超过没有被signal()唤醒,那5秒就会自动唤醒,这时候就会在if (isDone() || cur-start > timeout){ 被校验通过,从而break,退出。这时候在加个while条件是不是没必要。 还是说加个while条件是因为时间到点的时候自动唤醒后,Response可能是空,而且时间cur-start > timeout 不超时,所以才有必要进行while再一次判断isDone()是否有值。

    作者回复: while条件是编程范式,可以回去看管程原理,搞工程要多重防护。超时后当然很有可能resp是空的

    2019-04-03
    8
  • 水目沾
    这是一对一的关系,肯定只需要 signal。每个线程都是相互独立的,lock 和 condition 也是各自独享的。

    作者回复: 一对一的关系用signalall也不是不可以

    2019-04-02
    6
  • ycfHH
    作为一个完全不懂dubbo的新人,我很好奇是什么bug能让signal改成signalAll,因为不管怎么看都感觉signal就已经可以了啊(虽然使用signalall也不错)

    作者回复: 优化而已

    2019-05-06
    3
    5
  • 7
    老师,有个疑问 为什么要判断done!=null呢?这个条件不是永远为true吗。

    作者回复: 最新的代码他们已经改过来了😃

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