1、为什么使用管程?
信号量可以提供线程间的互斥和合作,但是使用信号量来设计一个正确的程序时很困难的,semWait和semSignal可能会分布在整个程序中,很难看出信号量上的这些操作所产生的整体效果。
管程(monitor)是一种程序设计语言结构,它提供与信号量相同的功能,但更容易控制。
2、什么是管程?
管程是包含共享变量以及对共享变量的操作方法的过程,让它们支持并发。
管程的特点:
①共享变量只能被管程中的方法访问,外部过程不能访问;
②一个线程只能通过调用管程的一个方法进行管程;
③任何时候,只能有一个线程在管程中执行,调用管程的其他线程阻塞等待。
前两点特点与面向对象语言中对象的特点很相似。因此,java中使用了管程模型,java中的管程就是管理类的成员变量和成员方法,让这个类线程安全。
3、管程提供的互斥和同步
1)管程中的共享变量每个只能被一个线程访问,因此对共享变量的访问是互斥的。
比如,管程将共享变量queue这个队列和相应的入队enq()、出队deq()操作封装起来,线程A和线程B只能通过调用方法enq()和deq()来访问共享变量queue,而这两个方法不会同时执行,因为只能有一个线程进入管程执行。
2)管程使用条件变量来支持同步。条件变量包含在管程中,并且只能在管程中被访问。两个函数用于操作条件变量:
- cwait():管程内的执行线程在条件 x 上阻塞,管程现在可以被另一线程使用;
- csignal():唤醒之前因条件 x 而阻塞的一个线程,如果没有这样的线程,则什么都不做。
当一个线程在管程中执行时,它可能会发现自己不满足某一条件x,通过调用cwait(x)把自己暂时阻塞在条件 x 上,然后它被放入条件 x 的阻塞队列。
当管程中的另一执行线程发现条件变量 x 发送改变,则发送csignal(x),唤醒条件 x 的阻塞队列上的线程。
4、Hoare模型
在管程发展过程中,先后出现过三种不同的管程模型:Hasen模型、Hoare模型和MESA模型。这里先介绍Hoare模型。
Hoare定义的管程,要求在条件阻塞队列上至少有一个线程,当另一个线程改变了该条件,并发送csignal时,立即运行该条件阻塞队列中的一个线程。因此,产生csignal的线程只能进行如下操作:
- 如果调用csignal方法是管程中的最后一步,则调用完成后立即退出管程 (Hasen模型强制使用这种方式);
- 如果调用csignal方法后,还需要继续在管程中执行,此时必须将自身阻塞在管程中。
下面是Hoare管程的一个例子( java实现 )。
示例代码1:
public class BlockQueue<T>{
final Lock lock = new ReentrantLock();
//条件变量:队列不满
final Condition notFull = lock