进程同步互斥工具:管程

一. 什么是管程

管程(Monitors) 指的是管理共享变量以及对共享变量的操作过程,让他们支持并发。它是 Java 语言在1.5之前提供的唯一并发原语,而 Java 1.5之后提供的 SDK 并发包也是以管程技术为基础的。

二. 为什么使用管程

信号量机制本身存在着缺点:进程自备同步操作,P(S) 和 V(S) 操作大量分散在各个进程中,不易管理,易发生死锁。而管程封装了同步操作,对进程隐蔽了同步细节,简化了同步功能的调用界面,使得用户编写并发程序如同编写顺序(串行)程序一样简单。

引入管程机制的目的:

  • 把分散在各进程中的临界区集中起来进行管理;
  • 防止进程有意或无意的违法同步操作;
  • 便于用高级语言来书写程序,也便于程序正确性验证。

三. 管程如何解决并发问题

在并发编程领域有两大核心问题:一个是互斥,即同一时刻只允许一个线程访问共享资源;另一个是同步,即线程之间如何通信和协作。这两个问题都可以通过管程来解决。

我们今天重点介绍一下现在最广泛应用的管程模型:MESA模型

1. 管程对互斥问题的解决

在这里插入图片描述
如上图所示:

  • 管程 X 封装了共享变量 queue 队列入队 enq()出队 deq() 操作;
  • 线程 A线程 B 如果想访问共享变量 queue,只能通过调用管程提供的 enq()deq() 方法来实现;
  • enq()deq() 具有互斥性,只允许一个线程进入管程。

2. 管程对同步问题的解决

在这里插入图片描述
最外层的框代表封装,当多个线程同时试图进入管程内部时,只允许一个线程进入,其它线程则需要在入口等待队列中等待。

管程里引入了条件变量的概念,每个条件变量都对应一个等待队列。比如上图中的条件变量 A 和条件变量 B 分别都有自己的等待队列。

  • 假如存在一个线程 T1 需要执行出队操作,那只有队列不为空时才能成功执行。此时,队列不为空这个前提条件就是管程里的条件变量。

  • 如果线程 T1 进入后发现队列是空的,就需要去“队列不空” 这个条件变量的等待队列中等待。

  • 而当另外一个线程 T2 执行入队操作,操作执行成功之后,“队列不空”这个条件对于线程T1来说已经满足了,此时线程 T2 要通知 T1 ,告诉它需要的条件已经满足了。

  • 当线程 T1 得到通知后,会从等待队列里面出来,但是出来之后不马上执行,而是重新进入入口等待队列里面进行等待。

其中,T1 去条件变量的等待队列中等待,是通过调用 wait() 实现的;

线程 T2 通知 T1 “队列不空” ,是通过调用 notify() 实现的。

notify() 和 notifyAll() 的区别在于,notify() 可以通知等待队列中的一个线程,而 notifyAll() 可以通知等待队列中的所有线程。

需要注意的地方:

1、对于 MESA 管程来说,有一个编程范式,就是需要在一个 while 循环里面调用 wait()。这个是 MESA 管程特有的。

while(条件不满足) {
    
    
 wait();
}

2、尽量使用 notifyAll() 而不是 notify(),只有满足以下3种条件时,才能使用 notify()

  • 所有等待线程拥有相同的等待条件;
  • 所有等待线程被唤醒后,执行相同的操作;
  • 只需要唤醒一个线程。

四. 管程的局限性

大多数常用的编程语言中没有实现管程,如果某种语言本身不支持管程,那么加入管程是很困难的。

猜你喜欢

转载自blog.csdn.net/j1231230/article/details/113883593