FSP语言学习(六):Monitors 和条件 Synchronization

目录

1. 引言

2. Condition Synchronization

2.1 Car Park Model

2.2 Car Park Program

2.3 Condition Synchronization in Java

3. Semaphores

3.1 Modeling Semaphores

3.2 Semaphores in Java

4. 有界缓存

4.1 Bounded Buffer Model

4.2 Bounded Buffer Program

4.3 嵌套监控器问题

4.4 Monitor Invariants


1. 引言

监视器是用于并发编程的语言特性。监视器封装了数据,这些数据只能被监视器的访问程序观察和修改。一次只能有一个访问程序被激活。因此,一个访问程序对封装在监视器中的数据变量有互斥的访问权。监视器听起来应该很熟悉,因为我们已经在之前的学习中看到了监视器的概念,尽管使用了不同的术语解释。一个对象满足了监视器的数据访问要求,因为它封装了数据,如果声明为私有,则只能由对象的方法访问。这些方法可以被同步以提供互斥的访问。因此,监视器在Java中被简单地表示为一个拥有同步方法的类。

除了确保对其封装的数据的访问是互斥的,监控器还支持条件同步。条件同步,正如这个术语所暗示的,允许监控器阻塞线程,直到一个特定的条件成立,比如一个计数变成非零,一个缓冲区变成空或者新的输入变得可用。本篇博文介绍了监视器中的条件同步是如何建模的,以及如何在Java中实现的。

2. Condition Synchronization

我们用一个简单的例子来说明条件同步的问题。一个停车场需要一个控制器,它只允许汽车在停车场未满时进入,并且为了保持一致性,不允许汽车在停车场内没有汽车时离开。下图是我们对停车场进行Java模拟的一个快照。

它描述了停车场已满的情况,障碍物被放下,不再允许其他车辆进入。汽车的到达和离开是由不同的线程模拟的。在图中,出发的线程已经停止,以允许停车场满员。因此,到达线程被阻止,无法继续前进。

2.1 Car Park Model

对一个系统进行建模的第一步是决定哪些事件或动作是值得关注的。在停车场系统中,我们可以抽象出一些细节,如显示面板的旋转和显示线程的启动和停止。因此,我们省略了建模中与运行、旋转、暂停和终止线程有关的动作。相反,我们只关注两个动作:汽车到达停车场和汽车离开停车场。这些动作分别被命名为arrivedepart

下一步是确定进程。这些进程是到达进程离开进程和控制进入停车场的进程

到达进程和离开进程都是微不足道的。它们分别试图产生一连串的到达行动和一连串的离开行动。

停车场控制必须只允许在停车场有空间时发生到达行动,在停车场有车时发生离开行动。这表达了其他进程在与停车场交互时必须满足的同步条件。

下图给出了停车场模型。CARPARKCONTROL进程使用索引状态SPACES来记录停车场的可用车位数量。上面描述的控制要求已经用FSP的保护动作结构进行了建模。因此,在状态SPACES[0]中,不接受到达动作,在状态SPACES[N]中,不接受离开动作。

在下图中,停车场系统的行为被描述为一个LTS。这个LTS是由上图的模型直接生成的。它清楚地表明,在必须发生离开动作之前,最多可以接受四个到达动作。

2.2 Car Park Program

我们的并发系统模型将系统中的所有实体表示为进程

在把一个模型的行为实现为Java程序时,我们必须决定哪些实体是主动的,哪些是被动的。

所谓主动,我们指的是发起行动的实体;这被实现为一个线程。

所谓被动,我们指的是对行动作出反应的实体;这被实现为一个监视器。

正如我们将在随后的例子中看到的,在模型中哪些进程在实现中成为线程,哪些成为监视器,这个决定并不总是很明确。然而,在停车场的例子中,这个决定是明确的。发起到达和离开动作的进程ARRIVALS和DEPARTURES应该作为线程来实现。响应到达和离开动作的CARPARKCONTROL进程应该是一个监视器。下图描述了停车场程序的类结构。

为了简化类图,我们省略了由ThreadPanel管理的DisplayThread和GraphicCanvas线程。与程序的并发执行有关的类是两个Runnable类:Arrivals和Departures,以及控制到达和离开的CarPark Control 类。这些类的实例是由CarPark小程序start()方法创建的。

public void start() {
    CarParkControl c =
    new DisplayCarPark(carDisplay,Places);
    arrivals.start(new Arrivals(c));
    departures.start(new Departures(c));
}

Arrivals和Departures是ThreadPanel类的实例,carDisplay是CarParkCanvas的实例,如类图所示。

到达和离开类的代码列在下面的程序中。这些类使用ThreadPanel.rotate()方法,该方法将旋转显示段移动的度数作为其参数。如果停车场已满,CarParkControl类必须阻止到达线程对arrive()的激活,如果停车场已空,则阻止离开线程对depart()的激活。我们如何在Java中实现这一点?

class Arrivals implements Runnable {
  CarParkControl carpark;

  Arrivals(CarParkControl c) {carpark = c;}
public void run() {
    try {
        while(true) {
            ThreadPanel.rotate(330);
            carpark.arrive();
            ThreadPanel.rotate(30);
        }
    } catch (InterruptedException e){}
  }
}

class Departures implements Runnable {
    
CarParkControl carpark;

    Departures(CarParkControl c) {carpark = c;}

    public void run() {
        try {
            while(true) {
                ThreadPanel.rotate(180);
                carpark.depart();
                ThreadPanel.rotate(180);
            }
        } catch (InterruptedException e){}
    }
}

2.3 Condition Synchronization in Java

Java为每个监视器提供了一个线程等待集;实际上是每个对象,因为任何对象都可能有一个与之相关的监视器同步锁。以下方法是由Object类提供的,所有其他的类都是从Object类派生出来的。

public final void notify()

唤醒一个正在等待这个对象的等待集的单线程。

public final void notifyAll()

唤醒所有在此对象的等待集上等待的线程。

public final void wait() throws InterruptedException

等待另一个线程的通知。等待的线程会释放与监视器相关的同步锁。当被通知时,该线程必须等待重新获得监视器,然后再继续执行。

如果被当前不 "拥有 "监视器的线程(即之前没有通过执行同步方法或语句获得同步锁的线程)调用,则操作失败。我们指的是,当一个线程获得与监视器相关的互斥锁时,它就进入了监视器,当它释放锁时,就退出了监视器。从上面的定义可以看出,调用wait()的线程会退出监视器。这允许其他线程进入监视器,当满足适当的条件时,可以调用notify()或notifyAll()来唤醒等待的线程。下图中描述了wait()和notify()的操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值