Mutex, Semaphore and Monitor (2)

在上篇文章的最后,我们描述了CV(条件变量)的定义和使用方式,也曾说过Monitor事实上是基于CV的。那么,Monitor到底是怎样一种机制呢?

其实,与其说Monitor是一种机制,倒不如说它是一种风格(style),因为它并不是一种新的同步机制。Monitor所做的,就是把mutex和CV封装在一个对象里面,来保护这个对象的共有数据的访问,如下图所示:

其中Lock控制线程的进入,保证只有一个对象能拿到锁;而CV负责线程的等待、唤醒等操作;put和get是对shared data的一组访问方法。这种形式就是Monitor。

看到这个模型,你可能马上就想到,这不就是Java里面 最常见的线程间同步机制么?没错,就是这样。Java在Object类提供了一套方法:wait/notify/notifyAll,并且通过在成员方法钱加synchronized方法来实现mutex,是典型的Monitor。而且,可以看出,Monitor是Java最推荐使用的线程同步方式(使用关键字的形式和把wait/signal/broadcast实现在Object中,相比之下,信号量类就放在java.util.concurrent包里)。

使用Monitor的好处在于,mutex和CV对用户都是透明的。用户只需知道,处于synchronized保护下的代码都是互斥的,而线程在对象上进行等待或着被唤醒。所以,我们可以很方便的改写前一篇文章中,使用pthread的C程序。

public class Counter {

    private int count;

    public Counter(int count) {
        this.count = count;
    }
    public int get() {
        return this.count;
    }
    public void incr() {
        this.count++;
    }
}

class Checker implements Runnable {

    private Counter counter;

    public Checker(Counter c) {
        this.counter = c;
    }

    @Override
    public void run() {

        synchronized (counter) {
            System.out.println("The Checker get the lock and the count is " + counter.get());
            while(counter.get() < 10)
            try {
                counter.wait();
                System.out.println("The Checker is notified and re-get the lock, now the count is " + counter.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class Increaser implements Runnable {

    private Counter counter;

    public Increaser(Counter c) {
        this.counter = c;
    }
    @Override
    public void run() {

        while (counter.get() < 100) {
            synchronized (counter) {
                System.out.println("Increaser get the lock and the count is "+ counter.get());
                counter.incr();
                if (counter.get() >= 10) {
                    counter.notifyAll();
                }
            }
        }
    }
}

  在main函数里面开启一个Checker和一个Increaser后运行,观察打印结果,会发现一个很有趣的现象,Checker的第二次打印,即在wait被唤醒之后打印的count并不是10,也就是说当Counter==10后,Increaser调用了notifyAll,但是Checker并没有恢复执行。为什么呢?原因我们在上篇文章中也提到过。notifyAll并不会释放锁,而是当Increaser离开synchronized代码块后才会释放,但是由于Increaser会循环执行(代码里是执行100次),它会和即将唤醒的Checker再次竞争mutex锁,所以并不能保证Checker会立即得到锁醒来,甚至,很多时候,Increaser更易得到mutex锁,使得checker醒来时counter远大于10。在我的实验中,有几次运行甚至是100,即Increaser执行完了,checker才得到mutex锁。

解决这个问题也很简单,就是在notifyAll后面加上break,这样,Increaser会跳出循环,并且释放锁。当然,这是一个比较特殊的例子。更多的时候,比如说Increaser需要在Checker做完之后再处理一些任务呢?显然,我们可以在Increaser调用notifyAll之后,调用wait,让其释放锁并等待,然后在Checker恢复执行后,再唤醒Increaser。如此往复下去,我们可以实现让线程交叉执行,甚至然照一个指定的序列执行。关于这方面,可以看下《Thinking in Java》中,线程那一章一个洗车抛光的例子。

讲到这里,相信大家对这三种机制已经有了一个大概的认识了,剩下的就得在实际工作中去体会和总结了,实践出真知嘛。

时间: 2024-12-15 05:54:10

Mutex, Semaphore and Monitor (2)的相关文章

C#线程同步(3)- 互斥量 Mutex

什么是Mutex "mutex"是术语"互相排斥(mutually exclusive)"的简写形式,也就是互斥量.互斥量跟临界区中提到的Monitor很相似,只有拥有互斥对象的线程才具有访问资源的权限,由于互斥对象只有一个,因此就决定了任何情况下此共享资源都不会同时被多个线程所访问.当前占据资源的线程在任务处理完后应将拥有的互斥对象交出,以便其他线程在获得后得以访问资源.互斥量比临界区复杂,因为使用互斥不仅仅能够在同一应用程序不同线程中实现资源的安全共享,而且可以

Java多线程(2)线程锁

多线程访问同一个资源进行读写操作,就很容易出一些问题(比如我们常见的读者写者,生产者消费者模型)所以我们会选择对他们设置信号量或者加锁,来限制同一个时刻只有一个线程对某个对象进行操作. 多线程是一个蛮复杂的工作,锁加多了就算是看伪代码有的时候脑子都转不过来,所以不要随便加锁(如果对自己的脑子没太多自信的话 Synchronized Synchronized关键字的作用是实现线程间的同步,它就像我们用PV原语来解决进程互斥问题,然后用对象的wait(),notify()提供线程的同步功能. 它的使

Nagios利用NSClient++监控Windows主机(4)

在Nagios的libexec下有check_nt这个插件,它就是用来检查windows机器的服务的.其功能类似于check_nrpe.不过还需要搭配另外一个软件NSClient++,它则类似于NRPE.我们需要下载NSClient合适的版本,然后安装在被监控的windows主机上. Overview of NRPE NSClient++的原理如下图: 可以看到NSClient与nrpe最大的区别就是: NRPE: 被监控机上安装有nrpe,并且还有插件,最终的监控是由这些插件来进行的.当监控主

SpringMVC + Mybatis + SpringSecurity(权限控制到方法按钮) + Rest(服务) + Webservice(服务) + Quartz(定时调度)+ Lucene(搜索引擎) + HTML5 bootstrap + Maven项目构建绝对开源平台

框架整合: Springmvc + Mybatis + Shiro(权限) + REST(服务) + WebService(服务) + JMS(消息) + Lucene(搜搜引擎) + Quartz(定时调度) + Bootstrap Html5(支持PC.IOS.Android) 需要源码请加Q:3121026417   此处[源码获取地址] 框架简介: 项目Maven构建,真实大型互联网架构,做到高并发,大数据处理,整个项目使用定制化服务思想,提供模块化.服务化.原子化的方案,将功能模块进行

C++11多线程教学(一)

转载自:http://www.cnblogs.com/lidabo/p/3908705.html 本篇教学代码可在GitHub获得:https://github.com/sol-prog/threads. 在之前的教学中,我展示了一些最新进的C++11语言内容: 1. 正则表达式(http://solarianprogrammer.com/2011/10/12/cpp-11-regex-tutorial/) 2. raw string(http://solarianprogrammer.com/

第9章 线程编程(4)_线程同步1:互斥锁

5. 线程的互斥和同步 5.1 同步和互斥的概念 (1)线程同步:是一个宏观概念,在微观上包含线程的相互排斥和线程的先后执行的约束问题.解决同步方式一般采用条件变量和信号量. (2)线程互斥:线程执行的相互排斥(注意,它不关心线程间执行的先后顺序!).解决互斥一般使用互斥锁.读写锁和信号量. [编程实验]银行ATM(线程不安全的例子) //account.h #ifndef __ACCOUNT_H__ #define __ACCOUNT_H__ typedef struct { int code

Linux shell中的I/O重定向相关(转)

1. 基本概念(这是理解后面的知识的前提,请务必理解)  a. I/O重定向通常与 FD有关,shell的FD通常为10个,即 0-9: b. 常用FD有3个,为0(stdin,标准输入).1(stdout,标准输出).2(stderr,标准错误输出),默认与keyboard.monitor.monitor有关: c. 用 < 来改变读进的数据信道(stdin),使之从指定的档案读进: d. 用 > 来改变送出的数据信道(stdout, stderr),使之输出到指定的档案: e. 0 是 &

【DATAGUARD】物理dg配置客户端无缝切换--Data Guard Broker 的配置(1)

[DATAGUARD]物理dg配置客户端无缝切换 (八.1)--Data Guard Broker 的配置 一.2.2  实验环境介绍 项目 主库 dg库 db 类型 单实例 单实例 db version 11.2.0.3 11.2.0.3 db 存储 FS type FS type ORACLE_SID oradg11g oradgphy db_name oradg11g oradg11g 主机IP地址: 192.168.59.130 192.168.59.130 OS版本及kernel版本

Linux运维 第二阶段(十六)OS优化(1)

一.相关概念: OS optimization 1.understanding the linux operating system: CPU(central processing unit)三大核心部件:运算器.控制器.寄存器 运算器(ALU,arithmetic logic unit算术逻辑单元,算术运算.逻辑运算等) 控制器(control unit,控制指令,数据的存取过程,到什么地方加载数据,加载完成后放到什么地方通知运算器计算,计算出的结果如何取出来放到什么地方,由控制指令完成,程序