在前面的文章中,我们已经体验过synchronized的用法,并对锁的概念和原理做了简单的介绍。然而,你可能已经察觉到,有一个概念似乎总是和synchronized这两个概念如影相随,很多人也比较喜欢问它们之间的区别,这个概念就是Monitor,也叫监视器

所以,在讲解完synchronized、锁之后,文本将为你讲解Monitor,揭示它们之间那些公开的秘密,希望你不再迷惑。

首先,你要明白的是,Monitor作为一种同步机制,它并非Java所特有,但Java实现了这一机制。

为了具象地理解Monitor这一抽象概念,我们先来分析身边的一个常见场景。

一、从医院排队就诊机制理解Monitor

相信你一定有过去医院就诊的经历。我们去医院时,情况一般是这样的:

  • 首先,我们在门诊大厅前台或自助挂号机进行挂号
  • 随后,挂号结束后我们找到对应的诊室就诊
    • 诊室每次只能有一个患者就诊;
    • 如果此时诊室空闲,直接进入就诊;
    • 如果此时诊室内有患者正在就诊,那么我们进入候诊室,等待叫号;
  • 就诊结束后,走出就诊室,候诊室的下一位候诊患者进入就诊室。

这个就诊过程你一定耳熟能详,理解起来必然毫不费力气。我们做了一张图展示图下:

仔细看这幅图中的就诊过程,如果你理解了这个过程,你就理解了Monitor. 这么简单吗?不要怀疑自己,是的。你竟然早已理解Monitor机制

不要小看这个机制,它可是生活中的智慧体现。在这个就诊机制中,它起到了两个关键性的作用

  • 互斥(mutual exclusion ):每次只允许一个患者进入候诊室就诊;
  • 协作(cooperation):就诊室中的患者就诊结束后,可以通知候诊区的下一位患者。

明白了吗?你在医院就诊的过程竟然和Monitor的机制几乎一模一样。我们换个方式来描述Monitor在计算机科学中的作用:

  • 互斥(mutual exclusion ):每次只允许一个线程进入临界区;
  • 协作(cooperation):当临界区的线程执行结束后满足特定条件时,可以通知其他的等待线程进入。

而就诊过程中的门诊大厅就诊室候诊室则恰好对应着Monitor中的三个关键概念。其中:

  • 门诊大厅:所有待进入的线程都必须先在**入口(Entry Set)**挂号才有资格;
  • 就诊室:一个每次只能有一个线程进入的特殊房间(Special Room)
  • 候诊室:就诊室繁忙时,进入等待区(Wait Set)

我们把上面的图稍作调整,就可以看到Monitor在Java中的模样:

对比来看,相信你已经很直观地理解Monitor机制。再一回味,你会发现synchronized正是对Monitor机制的一种实现。而在Java中,每一个对象都会关联一个监视器

二、从synchronized源码感受Monitor

既然synchronized是对Monitor机制的一种实现,为了让你更有体感,我们可以写一段极简代码一探究竟。

这段代码极为简单,但是够用,我们在代码中使用了synchronized关键字:

public class SyncMonitorDemo {public static void main(String[] args) {Object o = new Object();synchronized (o) {System.out.println("locking...");}}
}

代码写好后,分别执行javac SyncMonitorDemo.javajavap -v SyncMonitorDemo.class,随后你就能得到下面这样的字节码:

Classfile SyncMonitorDemo.classLast modified May 26, 2021; size 684 bytesMD5 checksum e366920f22845e98c45f26531596d6cfCompiled from "SyncMonitorDemo.java"
public class cn.tao.king.juc.execises1.SyncMonitorDemominor version: 0major version: 49flags: ACC_PUBLIC, ACC_SUPER
Constant pool:#1 = Methodref          #2.#22         // java/lang/Object."<init>":()V#2 = Class              #23            // java/lang/Object#3 = Fieldref           #24.#25        // java/lang/System.out:Ljava/io/PrintStream;#4 = String             #26            // locking...#5 = Methodref          #27.#28        // java/io/PrintStream.println:(Ljava/lang/String;)V#6 = Class              #29            // cn/tao/king/juc/execises1/SyncMonitorDemo#7 = Utf8               <init>#8 = Utf8               ()V#9 = Utf8               Code#10 = Utf8               LineNumberTable#11 = Utf8               LocalVariableTable#12 = Utf8               this#13 = Utf8               Lcn/tao/king/juc/execises1/SyncMonitorDemo;#14 = Utf8               main#15 = Utf8               ([Ljava/lang/String;)V#16 = Utf8               args#17 = Utf8               [Ljava/lang/String;#18 = Utf8               o#19 = Utf8               Ljava/lang/Object;#20 = Utf8               SourceFile#21 = Utf8               SyncMonitorDemo.java#22 = NameAndType        #7:#8          // "<init>":()V#23 = Utf8               java/lang/Object#24 = Class              #30            // java/lang/System#25 = NameAndType        #31:#32        // out:Ljava/io/PrintStream;#26 = Utf8               locking...#27 = Class              #33            // java/io/PrintStream#28 = NameAndType        #34:#35        // println:(Ljava/lang/String;)V#29 = Utf8               cn/tao/king/juc/execises1/SyncMonitorDemo#30 = Utf8               java/lang/System#31 = Utf8               out#32 = Utf8               Ljava/io/PrintStream;#33 = Utf8               java/io/PrintStream#34 = Utf8               println#35 = Utf8               (Ljava/lang/String;)V
{public cn.tao.king.juc.execises1.SyncMonitorDemo();descriptor: ()Vflags: ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: invokespecial #1                  // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 3: 0LocalVariableTable:Start  Length  Slot  Name   Signature0       5     0  this   Lcn/tao/king/juc/execises1/SyncMonitorDemo;public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATICCode:stack=2, locals=4, args_size=10: new           #2                  // class java/lang/Object3: dup4: invokespecial #1                  // Method java/lang/Object."<init>":()V7: astore_18: aload_19: dup10: astore_211: monitorenter12: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;15: ldc           #4                  // String locking...17: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V20: aload_221: monitorexit22: goto          3025: astore_326: aload_227: monitorexit28: aload_329: athrow30: returnException table:from    to  target type12    22    25   any25    28    25   anyLineNumberTable:line 5: 0line 6: 8line 7: 12line 8: 20line 9: 30LocalVariableTable:Start  Length  Slot  Name   Signature0      31     0  args   [Ljava/lang/String;8      23     1     o   Ljava/lang/Object;
}
SourceFile: "SyncMonitorDemo.java"

javap是JDK自带的一个反汇编命令。你可以忽略其他不必要的信息,直接在结果中找到下面这段代码:

11: monitorenter
12: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
15: ldc           #4                  // String locking...
17: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
20: aload_2
21: monitorexit

看到monitorentermonitorexit指令,相信智慧的你已经看穿一切。

以上就是文本的全部内容,恭喜你又上了一颗星✨

夫子的试炼

  • 写一段包含synchronized关键字的代码,使用javap命令观察结果。

关于作者

关注公众号【庸人技术笑谈】,获取及时文章更新。记录平凡人的技术故事,分享有品质(尽量)的技术文章,偶尔也聊聊生活和理想。不贩卖焦虑,不兜售课程。

如果本文对你有帮助,欢迎点赞关注

青铜7:顺藤摸瓜-如何从synchronized中的锁认识Monitor.md相关推荐

  1. 常见的锁策略、synchronized中的锁优化机制

    一.常见的锁策略 锁策略,和普通程序猿基本没啥关系,和 "实现锁" 的人才有关系的 这里所提到的锁策略,和 Java 本身没关系,适用于所有和 "锁" 相关的情 ...

  2. Java—synchronized和ReentrantLock锁详解

    关注微信公众号:CodingTechWork,一起学习进步. 1 synchronized 1.1 synchronized介绍 synchronized机制提供了对每个对象相关的隐式监视器锁,并强制 ...

  3. 青铜5:一探究竟-从synchronized理解对象头中的锁

    在前面的文章<青铜4:synchronized用法初体验>中,我们已经提到锁的概念,并指出synchronized是锁机制的一种实现.可是,这么说未免太过抽象,你可能无法直观地理解锁究竟是 ...

  4. 线程安全(中)--彻底搞懂synchronized(从偏向锁到重量级锁)

    接触过线程安全的同学想必都使用过synchronized这个关键字,在java同步代码快中,synchronized的使用方式无非有两个: 通过对一个对象进行加锁来实现同步,如下面代码. synchr ...

  5. 为什么wait和notify只能在synchronized中?

    前言 wait和notify必须在synchronized块中,否则会抛出IllegalMonitorStateException. 原因 代码示例 class BlockingQueue {Queu ...

  6. Java synchronized 中的while 和 notifyAll

    转载自  Java synchronized 中的while 和 notifyAll 问题1 为什么是while 而不是if 大多数人都知道常见的使用synchronized代码: synchroni ...

  7. synchronized 中的 4 个优化,你知道几个?

    作者 | 王磊 来源 | Java中文社群(ID:javacn666) 转载请联系授权(微信ID:GG_Stone) synchronized 在 JDK 1.5 时性能是比较低的,然而在后续的版本中 ...

  8. 线程同步机制synchronized中锁的判断以及锁的作用范围

    当我们使用多个线程访问同一资源(可以是同一个变量.同一个文件.同一条记录等)的时候,若多个线程只有读操作,那么不会发生线程安全问题,但是如果多个线程中对资源有读和写的操作,就容易出现线程安全问题. 要 ...

  9. java自适应table_【进阶之路】包罗万象——JAVA中的锁

    导言 大家好,我是练习java两年半时间的南橘,下面是我的微信,需要之前的导图或者想互相交流经验的小伙伴可以一起互相交流哦. 在Java中,我们能接触到各种各样的锁,而每种锁因其特性的不同,在不同的的 ...

最新文章

  1. Vue-router进阶:导航守卫
  2. java jtabbedpane 关闭_JTabbedPane实现关闭按钮 | 学步园
  3. 图书馆为什么使用sip2协议_RFID无人智能借还书柜让微型图书馆“触手可及”
  4. java 普通类request_[Java教程]spring在普通类中获取session和request
  5. Origin绘制带误差棒的曲线(Error Band)
  6. 算法——分而治之及快速排序
  7. 接口自动化测试框架搭建(2、配置文件配置文件的读取)--python+HTMLTestRunnerCN+request+unittest+mock+db
  8. 对比赛打分现场进行硬件连线的拓扑图_【评委计分系统-双屏版】参考硬件连线
  9. .net 5和.net core_开源仿真器 EpicSim 运行 SM3_core
  10. 信号与线性系统分析(吴大正,郭宝龙)(3-单位脉冲/阶跃序列以及4-信号的运算)
  11. 【Qt】运行报错Could not create directory “E:\xxxx-Debug“
  12. 关于redis HSCAN count参数不生效的问题
  13. 用串口控制SIM900A打电话和发短信,遇到的一些问题总结。
  14. 2020神舟几号发射_2020广东事业单位考试:神舟飞船小知识的考点提炼
  15. iOS Framework Shell打包
  16. 解决IOS引入H5页面Safari浏览器工具栏会遮挡页面底部展示
  17. Godot Engine:用着色器实现通过开关隐藏部分模型
  18. Weka数据挖掘——选择属性
  19. ISCC2021—检查一下
  20. 沪漂大专程序员,一边跟刘畊宏健身,一边拿22k的offer

热门文章

  1. 帝国cms7.5内核自适应导航站模板制作教程记录
  2. WaitingList,倒计时工具,营造每一个期待
  3. 2021-11-11 用eclipse编写摇骰子
  4. js正则表达式之中文验证(转)
  5. 祛除雀斑的健康方法 - 生活至上,美容至尚!
  6. selenium自动化:如何下载浏览器对应的版本驱动
  7. DIY自己的开机动画
  8. 【贪心算法】舞蹈室安排
  9. 动手实现查找小工具:优化版FindEverything
  10. 如何免费申请阿里云SSL证书