什么是管程
- Java采用了管程技术,synchronized关键字及wait()、notify()、notifyAll()三个方法都是管程的组成部分
- 管程和信号量是等价的,管程和信号量之间可以互相实现
- 英文名:Monitor 直译为监视器
- 管程指的是管理共享变量以及对共享变量的操作过程,让他们支持并发
MESA模型(一个被广泛应用的模型)
- 还有Hasen模型、Hoare模型,MESA使用的范围最广
- Java 管程的实现参考的也是 MESA 模型
- MESA模型的主要组成:
- 在管程模型里,共享变量和对共享变量的操作被封装起来,只有一个入库,入口旁边有一个入口等待队列。
- 在管程模型里,条件变量也被封装,每个条件变量都对应有一个等待队列。
- 在并发编程领域,有两大核心问题:一个是互斥,即同一时刻只允许一个线程访问共享资源;另一个是同步,即线程之间如何通信、协作。这两大问题,管程都是能够解决的。
- 管程解决互斥问题
- 将共享变量及其对共享变量的操作统一封装起来。
- 管程解决线程间的同步问题
- 模拟阻塞队列的出队入队过程
wait() 的正确姿势
对于 MESA 管程来说,有一个编程范式,就是需要在一个 while 循环里面调用 wait()。
三个模型的区别:Hasen 模型、Hoare 模型和 MESA 模型的一个核心区别就是当条件满足后,如何通知相关线程。管程要求同一时刻只允许一个线程执行,那当线程 T2 的操作使线程 T1 等待的条件满足时,T1 和 T2 究竟谁可以执行呢?
- Hasen 模型里面,要求 notify() 放在代码的最后,这样 T2 通知完 T1 后,T2 就结束了,然后 T1 再执行,这样就能保证同一时刻只有一个线程执行。
- Hoare 模型里面,T2 通知完 T1 后,T2 阻塞,T1 马上执行;等 T1 执行完,再唤醒 T2,也能保证同一时刻只有一个线程执行。但是相比 Hasen 模型,T2 多了一次阻塞唤醒操作。
- MESA 管程里面,T2 通知完 T1 后,T2 还是会接着执行,T1 并不立即执行,仅仅是从条件变量的等待队列进到入口等待队列里面。这样做的好处是 notify() 不用放到代码的最后,T2 也没有多余的阻塞唤醒操作。但是也有个副作用,就是当 T1 再次执行的时候,可能曾经满足的条件,现在已经不满足了,所以需要以循环方式检验条件变量。
总结
- Java 参考了 MESA 模型,语言内置的管程(synchronized)对 MESA 模型进行了精简。MESA 模型中,条件变量可以有多个,Java 语言内置的管程里只有一个条件变量。
- Java 内置的管程方案(synchronized)使用简单,synchronized 关键字修饰的代码块,在编译期会自动生成相关加锁和解锁的代码,但是仅支持一个条件变量;而 Java SDK 并发包实现的管程支持多个条件变量,不过并发包里的锁,需要开发人员自己进行加锁和解锁操作。(synchronized和Lock的区别之一)