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

08 | 管程:并发编程的万能钥匙

wait()方法的参数
并发编程的核心问题
Java内置的管程方案
适用条件
使用notifyAll()
MESA管程特有的编程范式
阻塞队列的出队操作
阻塞队列的入队操作
wait()、notify()、notifyAll()
条件变量和等待队列
分诊流程类比
面向对象高度契合
统一封装共享变量及操作
Java管程的实现
Hoare模型
Hasen模型
管理对共享变量的操作
管理共享变量
C#
C/C++
Java语言
课后思考
总结
notify()何时可以使用
wait()的正确姿势
示例代码
同步问题解决
互斥问题解决
MESA模型
什么是管程
管程技术

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

并发编程这个技术领域已经发展了半个世纪了,相关的理论和技术纷繁复杂。那有没有一种核心技术可以很方便地解决我们的并发问题呢?这个问题如果让我选择,我一定会选择管程技术。Java 语言在 1.5 之前,提供的唯一的并发原语就是管程,而且 1.5 之后提供的 SDK 并发包,也是以管程技术为基础的。除此之外,C/C++、C# 等高级语言也都支持管程。
可以这么说,管程就是一把解决并发问题的万能钥匙。

什么是管程

不知道你是否曾思考过这个问题:为什么 Java 在 1.5 之前仅仅提供了 synchronized 关键字及 wait()、notify()、notifyAll() 这三个看似从天而降的方法?在刚接触 Java 的时候,我以为它会提供信号量这种编程原语,因为操作系统原理课程告诉我,用信号量能解决所有并发问题,结果我发现不是。后来我找到了原因:Java 采用的是管程技术,synchronized 关键字及 wait()、notify()、notifyAll() 这三个方法都是管程的组成部分。而管程和信号量是等价的,所谓等价指的是用管程能够实现信号量,也能用信号量实现管程。但是管程更容易使用,所以 Java 选择了管程。
管程,对应的英文是 Monitor,很多 Java 领域的同学都喜欢将其翻译成“监视器”,这是直译。操作系统领域一般都翻译成“管程”,这个是意译,而我自己也更倾向于使用“管程”。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

管程技术是并发编程的核心技术,被誉为并发编程的“万能钥匙”。它通过统一封装共享变量及其操作来支持并发,解决了互斥和同步两大核心问题。MESA模型是广泛应用的管程模型,其核心特点是条件变量和等待队列的引入,以及使用wait()和notify()来实现线程间的同步。管程模型与面向对象高度契合,且在Java中得到了广泛应用。在MESA管程中,需要在一个while循环里面调用wait(),以确保条件满足后正确通知相关线程。相比其他管程模型,MESA模型的notify()不需要放在代码的最后,同时也避免了多余的阻塞唤醒操作。然而,需要注意的是,当线程再次执行时,可能之前满足的条件现在已经不再满足,因此需要以循环方式检验条件变量。管程技术在Java等高级语言中得到了广泛支持,具有重要的实用价值。 管程是一个解决并发问题的模型,理解这个模型的重点在于理解条件变量及其等待队列的工作原理。Java参考了MESA模型,语言内置的管程(synchronized)对MESA模型进行了精简。MESA模型中,条件变量可以有多个,Java语言内置的管程里只有一个条件变量。并发编程里两大核心问题——互斥和同步,都可以由管程来帮你解决。学好管程,理论上所有的并发问题你都可以解决,并且很多并发工具类底层都是管程实现的,所以学好管程,就是相当于掌握了一把并发编程的万能钥匙。 在MESA模型里,wait()方法增加了超时参数。这个参数的必要性是值得思考的,欢迎读者在留言区分享你的想法和思考过程。 总的来说,管程技术是并发编程中的重要工具,通过对条件变量和等待队列的合理使用,可以有效解决并发编程中的互斥和同步问题。掌握管程技术对于理解并发编程并解决相关问题具有重要意义。

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

全部留言(225)

  • 最新
  • 精选
  • Geek_be4cec
    本节说的可能并不好。该篇我看了三遍也没能完全看懂,于是自己搜索java管程相关的技术文章,才大致对管程有了个认知,总结如下: 1.管程是一种概念,任何语言都可以通用。 2.在java中,每个加锁的对象都绑定着一个管程(监视器) 3.线程访问加锁对象,就是去拥有一个监视器的过程。如一个病人去门诊室看医生,医生是共享资源,门锁锁定医生,病人去看医生,就是访问医生这个共享资源,门诊室其实是监视器(管程)。 4.所有线程访问共享资源,都需要先拥有监视器。就像所有病人看病都需要先拥有进入门诊室的资格。 5.监视器至少有两个等待队列。一个是进入监视器的等待队列一个是条件变量对应的等待队列。后者可以有多个。就像一个病人进入门诊室诊断后,需要去验血,那么它需要去抽血室排队等待。另外一个病人心脏不舒服,需要去拍胸片,去拍摄室等待。 6.监视器要求的条件满足后,位于条件变量下等待的线程需要重新在门诊室门外排队,等待进入监视器。就像抽血的那位,抽完后,拿到了化验单,然后,重新回到门诊室等待,然后进入看病,然后退出,医生通知下一位进入。 总结起来就是,管程就是一个对象监视器。任何线程想要访问该资源,就要排队进入监控范围。进入之后,接受检查,不符合条件,则要继续等待,直到被通知,然后继续进入监视器。

    作者回复: 👍

    2019-08-01
    36
    382
  • 密码123456
    有hasen 是执行完,再去唤醒另外一个线程。能够保证线程的执行。hoare,是中断当前线程,唤醒另外一个线程,执行玩再去唤醒,也能够保证完成。而mesa是进入等待队列,不一定有机会能够执行。

    作者回复: 我觉得你真的理解了!!!!

    2019-03-16
    9
    146
  • 三圆
    看了三遍,终于明白了管程MESA模型了,刚开始一直在想线程T1执行出队是什么意思?到底是哪个队列,是入口等待队列,还是条件等待队列,后来理解了都不是。这个队列应该理解为JDK里面的阻塞队列,里面存在的是共享数据,线程T1,T2分别去操作里面的共享数据,执行数据的入队,出队操作,当然这些操作是阻塞操作。当线程T1对阻塞队列执行数据出队操作时,进入管程,发现阻塞队列为空,此时线程T1进入阻塞队列不为空这个条件的条件等待队列,此时,其他线程还是可以进入管程的,比如T2进来了,对阻塞队列执行数据插入操作,这时就会致使线程T1从条件等待队列出来,进入入口等待队列,准备再一次进入管程……至于wait方法的参数,还是有必要的,因为可能线程需要的条件可能一直无法满足!

    作者回复: 例子选的不好,让你误解了...

    2019-03-16
    14
    98
  • Hour
    老师,针对条件变量的while循环,还是不太理解,您说是范式,那它一定是为了解决特定的场景而强烈推荐的,也有评论说是为了解决虚假唤醒,但唤醒后,不也是从条件的等待队列进入到入口的等待队列,抢到锁后,重新进行条件变量的判断,用if完全可以啊,为什么必须是while,并且是范式? 望老师赐教!

    作者回复: code1; if (条件不满足) wait() code2; 当调用wait()时,阻塞。被唤醒时,就直接执行code2了,没机会重新判断。

    2019-03-22
    14
    70
  • linqw
    管程的组成锁和0或者多个条件变量,java用两种方式实现了管程①synchronized+wait、notify、notifyAll②lock+内部的condition,第一种只支持一个条件变量,即wait,调用wait时会将其加到等待队列中,被notify时,会随机通知一个线程加到获取锁的等待队列中,第二种相对第一种condition支持中断和增加了时间的等待,lock需要自己进行加锁解锁,更加灵活,两个都是可重入锁,但是lock支持公平和非公平锁,synchronized支持非公平锁,老师,不知道理解的对不对

    作者回复: 总结很全面!

    2019-04-07
    3
    61
  • 小李子
    wait() 不加超时参数,相当于得一直等着别人叫你去门口排队,加了超时参数,相当于等一段时间,再没人叫的话,我就受不了自己去门口排队了,这样就诊的机会会大一点,是这样理解吧?

    作者回复: 挺形象,就诊机会不一定大,但是能避免没人叫的时候傻等

    2019-04-05
    5
    45
  • 佑儿
    老师,您好,结合第六讲,我的理解是:简单来说,一个锁实际上对应两个队列,一个是就绪队列,对应本节的入口等待队列,一个是阻塞队列,实际对应本节的条件变量等待队列,wait操作是把当前线程放入条件变量的等待队列中,而notifyall是将条件变量等待队列中的所有线程唤醒到就绪队列(入口等待队列)中,实际上哪个线程执行由jvm操作,我这样的理解对吗?

    作者回复: 对

    2019-03-20
    6
    37
  • CCC
    MESA模型和其他两种模型相比可以实现更好的公平性,因为唤醒只是把你放到队列里而不保证你一定可以执行,最后能不能执行还是要看你自己可不可以抢得到执行权也就是入口,其他两种模型是显式地唤醒,有点内定的意思了。

    作者回复: 内定都出来了,真是理论联系生活

    2019-03-17
    4
    24
  • 长脖子树
    没有看懂的可以看下 coursera 上北京大学陈老师讲的课程 MESA 管程 https://www.coursera.org/lecture/os-pku/mesaguan-cheng-Fya0t

    作者回复: 👍

    2020-04-14
    20
  • 红衣闪闪亮晶晶
    老师,我能明白如果t1线程被唤醒后再次进入等待队列,但是可能再次走到条件变量那里再次因为条件不满足随后再次开始等待,所以需要增加超时,所以当我给wait加了超时,时间到了以后t1再次开始while中的判断,如果满足便自己回到入口等待队列? 我这样理解对吗?

    作者回复: 如果没超时,A线程wait了,由于代码的bug,没有其他线程notify,就会导致A一直wait。增加超时之后,A线程可以自己来决定是否继续等待。这样代码的健壮性会更好

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