Java中的管程

为了带你精通 Java AQS,我画了 40 张图,从管程模型讲起! AQS 使用 FIFO 队列实现了一个锁相关的并发器模板,可以基于这个模板来实现各种锁,包括独占锁、共享锁、信号量等。AQS 中,有一个核心状态是 waitStatus,这个代表节点的状态,决定了当前节点的后续操作,比如是否等待唤醒,是否要唤醒后继节点。AQS 基于双向队列实现了入口等待队列,基于 state 变量实现了各种并发锁,上篇文章讲了入口等待队列,而这篇文章主要讲了基于 AQS 的并发锁原理。 阅读详情

title: Java中的管程
date: 2019-06-29 15:23:46
tags:
- Java
- 管程

Java是利用管程解决并发编程问题的,那么究竟什么是管程?而它又是如何解决并发问题的呢?

什么是管程

管程,英文名是 Monitor ,因此有的时候会被翻译为监视器。其实你也许很早就接触到这个概念了,比如 synchronized
关键字,很多文章就介绍过其原理是使用了监视器,只是你那个时候还并不知道监视器管程,其实是一回事。

我们来看看维基百科上的概念:

管程 (英语:Monitors,也称为监视器) 是一种程序结构,结构内的多个子程序(对象或模块)形成的多个工作线程互斥访问共享资源。

感觉这句话听得有点迷糊,但下面这句话应该就很好理解了:

管程提供了一种机制,线程可以临时放弃互斥访问,等待某些条件得到满足后,重新获得执行权恢复它的互斥访问。

我的理解是:我们通过管程管理 Java 中的类,使得类是线程安全的。

这应该是管程最终要达到的效果,那么,它是怎么做到的呢?

管程模型

管程这个概念最早来源于操作系统,操作系统发展了那么多年,管程的实现也有多种方式,主流的有三种:Hasen模型Hoare模型MESA模型, Java 中借鉴的是MESA模型,让我们来重点看一下。

谈到MESA模型,就不得不提到并发主要解决2个核心问题:一个是互斥,即同一时刻只允许一个线程访问共享资源;另一个是同步,即多个线程之间如何通信、协作。

如何解决互斥呢?我们可以在操作共享变量之前,增加一个等待队列,每一个线程想要操作共享变量的话,都需要在等待队列中等待,直到管程选出一个线程操作共享变量。

那又是如何解决同步的呢?线程在操作共享变量时候,它不一定是直接执行,可能有一些自己的执行条件限制(比如取钱操作要求账户里一定要有钱,出队操作要求队列一定不能是空的),我们将这些限制称之为条件变量,每一个条件变量也有自己对应的等待队列,当线程发现自己的条件变量不满足时,就进入相应的等待队列中排队,直至条件变量满足,那么其等待队列中的线程也不会是立马执行,而是到最开始共享变量对应的等待队列中再次排队,重复之前的过程。

可以参考下面这幅图:

理论说了那么多,还是来看看用代码是如何实现的吧

实现

首先可以自定一个支持并发的队列

public class MyQueen {

    // 共享变量(任何操作之前,都需要获得该锁才可以执行)
    private final Lock lock = new ReentrantLock();

    // 条件变量:队列不满
    private final Condition notFull = lock.newCondition();

    // 条件变量:队列不空
    private final Condition notEmpty = lock.newCondition();

    /**
     * 存储队列的容器
     */
    private final LinkedList<Integer> list = new LinkedList<>();

    /**
     * 最大容量
     */
    private int capacity;

    /**
     * 当前容器中存储的数量
     */
    private int size;

    public MyQueen(int capacity) {
        this.capacity = capacity;
        this.size = 0;
    }

    /**
     * 入队
     */
    public void enter(int value) {
        lock.lock();
        try {
            // 如果队列已满,则需要等到队列不满
            while (size >= capacity) {
                notFull.await(1, TimeUnit.MILLISECONDS);
            }

            // 入队
            list.add(value);
            size++;
            System.out.println(value + " has bean entered");
            // 通知可以出队
            notEmpty.signal();
        } catch (InterruptedException e) {
        } finally {
            lock.unlock();
        }
    }

    /**
     * 出队
     */
    public int dequeue() {
        Integer result = null;
        lock.lock();
        try {
            // 如果队列已空,则需要等到队列不空
            while (size <= 0) {
                notEmpty.await(1, TimeUnit.MILLISECONDS);
            }

            // 出队
            result = list.removeFirst();
            size--;
            System.out.println(result + " has bean dequeued");
            // 通知可以入队
            notFull.signal();
            return result;
        } catch (InterruptedException e) {
        } finally {
            lock.unlock();
        }
        return result;
    }

    public static void main(String[] args) {
        MyQueen myQueen = new MyQueen(3);
        new Thread(new Pruducer("producer1", myQueen, 0, 2)).start();
        new Thread(new Pruducer("producer2", myQueen, 2, 5)).start();
        new Thread(new Consumer("consumer2", myQueen, 5)).start();
        new Thread(new Consumer("consumer1", myQueen, 3)).start();
    }
}

定义生产者和消费者:

class Pruducer implements Runnable {

    private final MyQueen queen;

    /**
     * 该线程的名字
     */
    private final String name;

    /**
     * 开始的大小
     */
    private final int start;

    /**
     * 需要生产的资料个数
     */
    private final int size;

    public Pruducer(String name, MyQueen queen, int start, int size) {
        this.name = name;
        this.queen = queen;
        this.start = start;
        this.size = size;
    }


    @Override
    public void run() {
        for (int i = 1; i <= size; i++) {
            int now = start + i;
//            System.out.println(name + " produce : " + now + " start");
            queen.enter(now);
//            System.out.println(name + " produce : " + now + " end");
        }
    }
}

class Consumer implements Runnable {

    private final MyQueen queen;

    /**
     * 该线程的名字
     */
    private final String name;

    /**
     * 需要消费的资料个数
     */
    private final int size;

    public Consumer(String name, MyQueen queen, int size) {
        this.name = name;
        this.queen = queen;
        this.size = size;
    }


    @Override
    public void run() {
        for (int i = 1; i <= size; i++) {
//            System.out.println(name + " consume start");
            int result = queen.dequeue();
//            System.out.println(name + " consume : " + result + " end");
        }
    }
}

做一个测试的main方法:

    public static void main(String[] args) {
        MyQueen myQueen = new MyQueen(3);
        new Thread(new Pruducer("producer1", myQueen, 0, 2)).start();
        new Thread(new Pruducer("producer2", myQueen, 2, 5)).start();
        new Thread(new Consumer("consumer1", myQueen, 3)).start();
        new Thread(new Consumer("consumer2", myQueen, 5)).start();
    }

结果为:

1 has bean entered
2 has bean entered
3 has bean entered
1 has bean dequeued
2 has bean dequeued
3 has bean dequeued
4 has bean entered
5 has bean entered
6 has bean entered
4 has bean dequeued
5 has bean dequeued
6 has bean dequeued
7 has bean entered
8 has bean entered
9 has bean entered
7 has bean dequeued
8 has bean dequeued
9 has bean dequeued

虽然满足我想要的结果,但显示的内容有些奇怪,总是容器先被填满之后,然后容器被清空,感觉原因应该和可重入锁有关。

总结

以上就是我对于管程的一些理解,如果大家有什么疑问,欢迎在下方留言。
有兴趣的话可以看看我的私人博客,说不定会有意外的惊喜。

别再为PT100测温发愁了!手把手教你用ADS1247+STM32搞定高精度温度采集(附完整源码) 本文详细介绍了如何利用ADS1247高精度ADC与STM32微控制器构建PT100测温系统,实现±0.1℃的温度测量精度。内容涵盖硬件设计、寄存器配置、SPI通信优化及温度换算算法,特别适合工业自动化和精密仪器领域的嵌入式开发人员参考。 阅读详情

相关推荐

海康威视国内版监控设备改英文界面难题:原因解析与解决方案

跨境安防设备部署中,国内采购的海康威视监控设备常因仅支持中文界面导致海外用户无法操作。本文剖析了该问题的核心原因:海康威视对国内版和海外版设备实行区域锁定策略,国内版固件仅内置中文且无法通过常规方式切换语言。针对此问题,文章提供了三种解决方案:1)官方合规的海外版固件更换;2)低成本的操作界面翻译与远程协助;3)长期更换海外版设备的彻底解决方案。同时给出了跨境采购的注意事项,强调应提前确认设备版本、拒绝非官方改造并预留适配时间。

海康威视技术支持 3616

并发编程(六):管程

就可以看作一个条件变量)不满足,此时线程T1就可以调用wait()方法,并进入条件变量的等待队列,然后线程T2进入了管程中或执行了一个操作,发现T1不满足的某个条件满足了,此时T2就可以调用notify()/notifyAll()通知条件变量等待队列中的某个线程或全部线程,此时T1再去重新去从入口进入管程执行方法。synchronized 关键字修饰的代码块,在编译阶段会自动生成相关加锁和解锁的代码,但是仅支持一个条件变量,所以使用synchronized实际上只体现了管程的互斥,而同步却无法提现。

张豫湘的博客 743

截止2024年12月31日-A股公司ESG评级.xlsx

ESG评级是对企业在环境、社会和治理三个方面的表现进行评估的指标体系,目前国内外有多家ESG评级机构,国外如明晟(MSCI)、富时罗素(FTSE Russell)、彭博(BloombergESG)、标普全球(S&P Global)等,国内如万得(Wind)、商道融绿、华证指数等,不同机构的评分标准和方法各不相同,但大多关注前述三个核心领域的表现本次分享的数据包括华证、富时罗素、商道融绿、盟浪ESG评级数据,统计截至时间2024.12.31。旨在帮助投资者、公司及其他利益相关者评估企业在可持续发展和社会责任方面的表现## 一、数据介绍数据名称:A股上市公司-ESG评级数据数据年份:截止2024年12月31日数据范围:5405家上市公司数据格式:excel

Java多线程——共享模型之管程

Java多线程——共享模型之管程

weixin_42786301的博客 938

Java并发编程:基础-管程

临界区代码就是访问共享资源时又有读又有写的代码片段有两种关键字对资源进行保护:synchronized、lock(ReentrantLock)明白互斥和同步的区别:互斥:临界区代码不被上下文切换而交错,保证代码的原子性同步:某个线程条件不满足时等待,条件满足后继续运行管程即Monitor。

Oceáno的博客 1576

java并发-管程

什么是管程 Java并发编程模拟管程(霍尔Hoare管程、汉森Hansan管程、MESA管程) 参考URL: https://blog.csdn.net/qq_34666857/article/details/103189107 管程并发编程的万能钥匙 参考URL: https://time.geekbang.org/column/article/86089 Java中,每个对象其实都一个Monitor(java中翻译为监视器),Java中提供的synchronized关键字及wait()、notify(

西京刀客 1593

java管程 实现,Java中的管程模型

Java中的管程模型操作系统使用信号量解决并发问题,Java选择使用管程(Monitor)解决并发问题。信号量和管程是等价的,可以使用信号量实现管程,也可以使用管程实现信号量。管程就是指管理共享变量,以及对共享变量的相关操作。具体到 Java 语言中,管程就是管理类的成员变量和方法,让这个类是线程安全的。管程的发展史中,先后出现过三种管程模型,Hasen 模型、Hoare 模型和 MESA 模型,...

weixin_39996035的博客 502

java 管程通信_JAVA中的管程

java在1.5之前只提供了synchronized,wait,notfiy,notfiyAll3种实现并发的源语,他们实际上是管程的组成部分。对应的英文是Monitor,他表示管理共享变量以及操作共享变量,使其支持并发的过程。java的实现–MESA模式在管程的发展史上出现过3中管程模型,Hasen、Hoare、MESA。java采用的是MESA模型在并发变成领域,有俩大核心问题:互斥–一个共享...

weixin_30365269的博客 287

java中的管程

前言 ​ 并发编程这个技术领域已经发展了半个世纪了,相关的理论和技术纷繁复杂。那有没有一种核心技术可以很方便地解决我们的并发问题呢?这个问题如果让我选择,我一定会选择管程技术。Java 语言在 1.5 之前,提供的唯一的并发原语就是管程,而且 1.5 之后提供的 SDK 并发包,也是以管程技术为基础的。除此之外,C/C++、C# 等高级语言也都支持管程。可以这么说,管程就是一把解决并发问题的万能钥...

weixin_33860528的博客 406

Java中的管程Monitor

一、什么是管程 管程是一个抽象的概念模型,其封装了一套对共享资源访问的模型,目的是通过一个模型来管理共享资源的访问过程,让可能存在多个进程或线程同时访问一个共享资源时能达到"互斥"和"同步"的效果,管程实现管程模型必须达到下面几点要求: 管程中的共享变量对于外部都是不可见的,只能通过管程才能访问对应的共享资源 管程是互斥的,某个时刻只能允许一个进程或线程访问共享资源 管程中需要有线程等待队列和相应的等待或唤醒操作 必须有一种方法使进程无法继续运行时被阻塞 二、Java管程 Java是通过synchron

kking_edc的博客 1040

Java并发编程实战_Java并发编程中的管程(Monitor)模型

简述 A Monitor defines a lock and zero or more condition variables for managing concurrent access to shared data.管程(Monitor)是一种和信号量(Sophomore)等价的同步机制。它在Java并发编程中也非常重要,虽然程序员没有直接接触管程,但它确实是synchronized和wai...

weixin_40003451的博客 378

java monitor lock_说一说管程(Monitor)及其在Java synchronized机制中的体现

什么是管程管程首先由霍尔(C.A.R.Hoare)和汉森(P.B.Hansen)两位大佬提出,是一种并发控制机制,由编程语言来具体实现。它负责管理共享资源以及对共享资源的操作,并提供多线程环境下的互斥和同步,以支持安全的并发访问。“共享资源以及对共享资源的操作”在操作系统理论中称为critical section,即临界区。管程能够保证同一时刻最多只有一个线程访问与操作共享资源(即进入临界区)。在...

weixin_28769989的博客 336

Java并发中的管程:Lock和Condition

指的是线程可以重复获取同一把锁。

2301_81948543的博客 1146

Java中的管程锁定技术:深入解析与应用

Java并发编程中,管程(Monitor)是一个重要的概念,它提供了一种机制来同步对共享资源的访问,从而避免竞态条件和确保线程安全。虽然Java语言本身并没有直接使用“管程”这个术语,但Java的关键字和Object类的wait()notify()方法共同实现了管程的基本功能。本文将深入解析Java中的管程锁定技术,包括其原理、使用场景以及最佳实践。

m0_61431263的博客 333

java 管程

JVM中同步是基于进入和退出监视器对象(Monitor)来实现的,每个对象实例都会有一个Monitor对象,和java对象一同创建和销毁,底层是C++。管程即Monitor(监视器),也叫锁,Monitor其实是一种同步机制,保证只有一个线程可以访问被保护的数据和代码。持有管程才可以进入同步代码块,退出时需要释放管程

weixin_55038914的博客 763

学习笔记:Java 并发编程②_管程

本博客内容:共享问题分析;synchroinzed;线程安全分析;Monitor;wait / notify;Park / Unpark;分析线程状态的转换;活跃性;ReentrantLock。

做笔记,记录学习中遇到的问题。 1734

java线程共享模型之管程(锁及其操作)

synchronized锁的常见问题,与认识。

su_xu_chao的博客 1252

Java并发编程:管程并发编程的万能钥匙

管程提供了一种结构化的方法来处理并发编程中的同步问题。通过使用waitnotify和notifyAll方法,以及关键字,可以有效地实现线程间的同步和通信。在Java中,管程的实现通常依赖于对象的互斥锁和条件变量,这使得它成为一个非常强大的工具,可以帮助开发人员编写健壮的并发程序。如果你有任何疑问或需要进一步的解释,请随时提问!

qq_33240556的博客 811

Java管程:Synchronized

消除锁是虚拟机另外一种锁的优化,这种优化更彻底,在JIT编译时,对运行上下文进行扫描,做逃逸分析,去除不可能存在竞争的锁(去掉了申请和释放锁的代码了)。进入EntryList的线程需要与其他线程争抢锁,抢到锁之后以排它方式执行同步代码块的代码,当一个线程被notify后,将从_WaitSet中移动到EntryList中。锁粗化是虚拟机对另一种极端情况的优化处理,通过扩大锁的范围,避免反复获取锁和释放锁。偏向锁:线程获取锁后,锁对象的Mark Word标记偏向锁,通过一个字段记录当前线程id,

2301_76607156的博客 333

进程、线程、管程、纤程、协程概念以及区别

进程 进程是指在操作系统中能独立运行并作为资源分配的基本单位,由一组机器指令、数据和堆栈等组成的能独立运行的活动实体。 进程在运行是需要一定的资源,如CPU、存储空间和I/O设备等。 进程是资源分配的基本单位,进程的调度涉及到的内容比较多(存储空间,CPU,I/O资源等,进程现场保护),调度开销较大,在并发的切换过程效率较低。为了更高效的进行调度,提出了比进程更轻量的独立运行和调度的基本单位。 线程 线程能独立运行,独立调度,拥有资源(..

海角12138的博客 1055
上一篇: 力扣-65不同路径
下一篇: Java服务器-Disruptor使用注意
death05
博客等级 码龄11年 68粉丝 148原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值