操做系统使用信号量解决并发问题,Java选择使用管程(Monitor)解决并发问题。信号量和管程是等价的,可使用信号量实现管程,也可使用管程实现信号量。java
管程就是指管理共享变量,以及对共享变量的相关操做。具体到 Java 语言中,管程就是管理类的成员变量和方法,让这个类是线程安全的。管程的发展史中,前后出现过三种管程模型,Hasen 模型、Hoare 模型和 MESA 模型,Java 使用的是 MESA 模型。编程
咱们用管程模型主要是解决并发编程中的两个核心问题,互斥和同步。互斥是指同一时刻只容许一个线程访问共享资源,同步则是指线程之间如何通讯、写做。安全
那么,Java 所采用的 MESA 模型是如何解决互斥和同步问题的呢?并发
管程模型解决互斥问题的方法是:将共享变量及对共享变量的操做统一封装起来。spa
以下图所示,管程 X 将共享变量 queue,及其入队出队操做 enq() 和 dep() 封装起来。线程 A 和线程 B 想要访问共享变量 queue,就须要经过 enq() 和 deq() 来实现,而 enq() 和 deq() 保证互斥,只容许一个线程进入管程。操作系统
MESA 模型解决同步问题能够类比去医院就医。患者首先须要排队等待医生叫好,医生诊断被叫到号的患者。期间,患者若是须要进行其余辅助的检查,好比说排个 X 光,就须要去等待拍 X 光的医生叫好。患者拍完 X 光以后,再次回到上一个医生那里,等待医生再次诊断。线程
管程模型与看医生的流程相似,管程入口处有一个等待队列。当多个线程试图进入管程内部的时候,只容许一个线程进入,其余线程在等待队列中等待。就和看医生的时候排队同样。3d
管程中还有一个条件变量的概念,每一个条件变量对应一个条件变量等待队列。好比说有一个条件变量 A,当执行线程 T1 时发现不知足条件变量 A,T1 就会进入条件变量 A 的等待队列中。就像去看医生,医生让你先去排个 X 光,就要去拍 X 光的地方排队。code
当执行线程 T2 时发现知足条件变量 A,就会唤醒条件变量 A 等待中的线程 T1,线程 T1 就会再次进入到入口等待队列。就像拍完 X 光的人,再去看医生。blog
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(); } } }
Java 参考了 MESA 模型,语言内置的管程(synchronized)对 MESA 模型进行了精简。MESA 模型中,条件变量能够有多个,Java 语言内置的管程里只有一个条件变量。
Java SDK 并发包实现的管程支持多个条件变量,不过并发包里的锁,须要开发人员本身进行加锁和解锁操做。