对应原书第 20 章:线程同步。
Java 在语言级别就支持了多线程,本章讲述了监视器,展示了 Java 虚拟机使用监视器的方式,以及指令集在数据的锁定和解锁方面是如何支持监视器的。
监视器
Java 中的监视器支持线程的互斥和协作。其中互斥是通过对象锁来实现的,协作是通过 Object 类的 wait 和 notify 方法来实现的。
监视器的模型如下:
监视器包括三个区域,中间的大方框包括一个单独的线程,是监视器的持有者。左边的小方框是入口区(entry set),右边的小方框是等待区(wait set)。
上图中的 ①-⑤ 编号对应的是线程与监视器交互的几道门。当一个线程到达监视区域的开始处时,它会通过 ① 号门进入监视器,发现自己处在那个称为 entey set 的小方框中:
- 如果此时没有任何线程持有监视器,也没有其他线程在 entry set 等待,那么这个线程会直接通过 ② 号门,并持有监视器,此时它作为监视器的持有者,会继续执行监视区域的代码
- 如果有另外的线程正只有监视器,这个新到达的线程必须在 entry set 等待,很可能在 entry set 已经有一些别的线程在等待了,此时这个线程被阻塞,也就不能执行监视区域的代码
上图中有 3 个线程在 entry set 里暂停,有 4 个线程在 wait set 里暂停,这些线程会一直在那里,直到监视器当前的持有者(即活动线程)释放监视器。活动线程有两条途径释放监视器:
- 执行完监视区域的代码 (此时会通过 ⑤ 号门退出监视器)
- 执行一个等待命令(wait 方法) (此时会通过 ③ 号门进入 wait set,从而释放监视器)
对象锁
在 Java 中,每个对象和类在逻辑上都是和一个监视器相关联的。对对象而言,它关联的监视器保护对象的实例变量;对类而言,它关联的监视器保护类的类变量。 为了实现监视器的排他性监视能力,Java 虚拟机为每一个对象和类都关联一个锁(有时候也被称为:互斥体,mutex),一个锁就像一种任何时候只允许一个线程“拥有”的特权。锁住一个对象,就是获取对象关联的监视器。 类锁也是用对象锁实现的,锁住一个类时,实际锁住的是那个类的 Class 对象。
参考: https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html https://howtodoinjava.com/java/multi-threading/multithreading-difference-between-lock-and-monitor/