管程 (英语:Moniters,也称为监视器) 是一种程序结构,结构内的多个子程序(对象或模块)形成的多个工作线程互斥访问共享资源。
这些共享资源一般是硬件设备或一群变量。管程实现了在一个时间点,最多只有一个线程在执行管程的某个子程序。
与那些通过修改数据结构实现互斥访问的并发程序设计相比,管程实现很大程度上简化了程序设计。
管程提供了一种机制,线程可以临时放弃互斥访问,等待某些条件得到满足后,重新获得执行权恢复它的互斥访问。
线程可能需要等待某个条件P为真,才能继续执行。在一个忙等待(busy waiting)循环中
while not( P ) do skip
将会导致所有其它进程都无法进入临界区使得该条件P为真,该管程发生死锁.
解决办法是条件变量(condition variables). 概念上,一个条件变量就是一个线程队列(queue), 其中的线程正等待某个条件变为真。
每个条件变量c关联着一个断言P_c. 当一个线程等待一个条件变量,该线程不算作占用了该管程,因而其它线程可以进入该管程执行,
改变管程的状态,通知条件变量c其关联的断言P_c在当前状态下为真.
Java中的同步方法与其他经典管程有本质差别:Java没有内嵌的条件变量。反之,Java提供了两个过程wait和notify ,分别与sleep和wakeup等价,
不过,当它们在同步方法中使用时,它们不受竞争条件约束。理论上,方法wait可以被中断,它本身就是与中断有关的代码。Java需要显式表示异常处理。在下文中,只要认为go_to_sleep就是去睡眠即可。
外部类(outer class)ProducerConsumer创建并启动两个线程,p和c。第二个类和第三个类producer和consumer分别包含生产者和消费者的代码。
最后,类our_monitor是管程,它有两个同步线程,用于在共享缓冲区中插入和取出数据项。
public class ProducerConsumer { static final int N = 100;// 定义缓冲区大小的常量 static Producer p = new Producer();// 初始化一个新的生产者线程 static Consumer c = new Consumer();// 初始化一个新的消费者线程 static Our_monitor mon = new Our_monitor();// 初始化一个新的管程 public static void main(String[] args) { p.start();// 生产者启动 c.start();// 消费者启动 } static class Producer extends Thread { public void run() {// 线程运行主代码 int item; while (true) {// 生产者循环 item = produce_item(); mon.insert(item); } } private int produce_item() { System.out.println("生产了1个"); return 1; }// 实际生产 } static class Consumer extends Thread { public void run() {// 线程运行主代码 int item; while (true) {// 消费者循环 item = mon.remove(); consume_item(item); } } private void consume_item(int item) { System.out.println("消费了1个"); }// 实际消费 } static class Our_monitor {// 管程 private int buffer[] = new int[N]; private int count = 0, lo = 0, hi = 0;// 计数器和索引 public synchronized void insert(int val) { if (count == N) go_to_sleep();// 若缓冲区满,则进入睡眠 buffer[hi] = val;// 向缓冲区中插入一个新的数据项 hi = (hi + 1) % N;// 设置下一个数据项的槽 count = count + 1;// 缓冲区的数据项又多了一项 if (count == 1) notify();// 如果消费者在休眠,则将其唤醒 } public synchronized int remove() { int val; if (count == 0) go_to_sleep();// 如果缓冲区空,进入休眠 val = buffer[lo];// 从缓冲区中取出一个数据项 lo = (lo + 1) % N;// 设置待取数据项的槽 count = count - 1;// 缓冲区的数据数目减少1 if (count == N - 1) notify();// 如果生产者正在休眠,则将其唤醒 return val; } private void go_to_sleep() { try {wait();} catch (InterruptedException exc) {}; } } }该程序中比较意思的部分是类our_monitor,它包含缓冲区、管理变量以及两个同步方法。当生产者在insert内活动时,它确信消费者不能在remove中活动,从而保证更新变量和缓冲区的安全,且不用担心竞争条件。变量count记录在缓冲区中数据项的数量。它的取值可以取从0到N-1之间任何值。变量lo是缓冲区槽的序号,指出将要取出的下一个数据项。类似地,hi是缓冲区中下一个将要放入的数据项序号。允许 lo = hi,其含义是在缓冲区中有0个或N个数据项。count的值说明了究竟是哪一种情形。
参考文献:
http://zh.wikipedia.org/zh-cn/%E7%9B%A3%E8%A6%96%E5%99%A8_(%E7%A8%8B%E5%BA%8F%E5%90%8C%E6%AD%A5%E5%8C%96)
现代操作系统
原文:http://blog.csdn.net/lgcssx/article/details/38964089