操作系统中的管程
如果你在大学学习过操作系统,你可能还记得管程(monitors)在操作系统中是很重要的概念。同样Monitor在java同步机制中也有使用。
管程 (英语:Monitors,也称为监视器) 是一种程序结构,结构内的多个子程序(对象或模块)形成的多个工作线程互斥访问共享资源。这些共享资源一般是硬件设备或一群变量。管程实现了在一个时间点,最多只有一个线程在执行管程的某个子程序。与那些通过修改数据结构实现互斥访问的并发程序设计相比,管程实现很大程度上简化了程序设计。 管程提供了一种机制,线程可以临时放弃互斥访问,等待某些条件得到满足后,重新获得执行权恢复它的互斥访问。
Java线程同步相关的Moniter
在多线程访问共享资源的时候,经常会带来可见性和原子性的安全问题。为了解决这类线程安全的问题,Java提供了同步机制、互斥锁机制,这个机制保证了在同一时刻只有一个线程能访问共享资源。这个机制的保障来源于监视锁Monitor,每个对象都拥有自己的监视锁Monitor。
先来举个例子,然后我们在上源码。我们可以把监视器理解为包含一个特殊的房间的建筑物,这个特殊房间同一时刻只能有一个客人(线程)。这个房间中包含了一些数据和代码。
如果一个顾客想要进入这个特殊的房间,他首先需要在走廊(Entry Set)排队等待。调度器将基于某个标准(比如 FIFO)来选择排队的客户进入房间。如果,因为某些原因,该客户客户暂时因为其他事情无法脱身(线程被挂起),那么他将被送到另外一间专门用来等待的房间(Wait Set),这个房间的可以可以在稍后再次进入那件特殊的房间。如上面所说,这个建筑屋中一共有三个场所。
总之,监视器是一个用来监视这些线程进入特殊的房间的。他的义务是保证(同一时间)只有一个线程可以访问被保护的数据和代码。
Monitor其实是一种同步工具,也可以说是一种同步机制,它通常被描述为一个对象,主要特点是:
对象的所有方法都被“互斥”的执行。好比一个Monitor只有一个运行“许可”,任一个线程进入任何一个方法都需要获得这个“许可”,离开时把许可归还。
通常提供singal机制:允许正持有“许可”的线程暂时放弃“许可”,等待某个谓词成真(条件变量),而条件成立后,当前进程可以“通知”正在等待这个条件变量的线程,让他可以重新去获得运行许可。
监视器的实现
在Java虚拟机(HotSpot)中,Monitor是基于C++实现的,由ObjectMonitor实现的,其主要数据结构如下:
ObjectMonitor(){
_header = NULL;
_count =0;
_waiters =0,
_recursions =0;
_object = NULL;
_owner = NULL;_WaitSet= NULL;_WaitSetLock=0;_Responsible= NULL ;
_succ = NULL ;
_cxq = NULL ;FreeNext= NULL ;_EntryList= NULL ;_SpinFreq=0;_SpinClock=0;OwnerIsThread=0;}
源码地址:objectMonitor.hpp
ObjectMonitor中有几个关键属性:
_owner:指向持有ObjectMonitor对象的线程
_WaitSet:存放处于wait状态的线程队列
_EntryList:存放处于等待锁block状态的线程队列
_recursions:锁的重入次数
_count:用来记录该线程获取锁的次数
当多个线程同时访问一段同步代码时,首先会进入_EntryList
队列中,当某个线程获取到对象的monitor后进入_Owner
区域并把monitor中的_owner
变量设置为当前线程,同时monitor中的计数器_count
加1。即获得对象锁。
若持有monitor的线程调用wait()
方法,将释放当前持有的monitor,_owner
变量恢复为null
,_count
自减1,同时该线程进入_WaitSet
集合中等待被唤醒。若当前线程执行完毕也将释放monitor(锁)并复位变量的值,以便其他线程进入获取monitor(锁)。如下图所示
ObjectMonitor类中提供了几个方法:
获得锁
void ATTR ObjectMonitor::enter(TRAPS){Thread*constSelf= THREAD ;void* cur ;//通过CAS尝试把monitor的`_owner`字段设置为当前线程
cur =Atomic::cmpxchg_ptr (Self,&_owner, NULL);//获取锁失败if(cur == NULL){assert(_recursions ==0,"invariant");assert(_owner ==Self,"invariant");// CONSIDER: set or assert OwnerIsThread == 1return;}// 如果旧值和当前线程一样,说明当前线程已经持有锁,此次为重入,_recursions自增,并获得锁。if(cur ==Self){// TODO-FIXME: check for integer overflow! BUGID 6557169.
_recursions ++;return;}// 如果当前线程是第一次进入该monitor,设置_recursions为1,_owner为当前线程if(Self->is_lock_owned ((address)cur)){assert(_recursions ==0,"internal state error");
_recursions =1;// Commute owner from a thread-specific on-stack BasicLockObject address to// a full-fledged "Thread *".
_owner =Self;OwnerIsThread=1;return;}// 省略部分代码。// 通过自旋执行ObjectMonitor::EnterI方法等待锁的释放for(;;){
jt->set_suspend_equivalent();// cleared by handle_special_suspend_equivalent_condition()// or java_suspend_self()EnterI(THREAD);if(!ExitSuspendEquivalent(jt))break;//// We have acquired the contended monitor, but while we were// waiting another thread suspended us. We don't want to enter// the monitor while suspended because that would surprise the// thread that suspended us.//
_recursions =0;
_succ = NULL ;exit(Self);
jt->java_suspend_self();}}
释放锁
void ATTR ObjectMonitor::exit(TRAPS){Thread*Self= THREAD ;//如果当前线程不是Monitor的所有者if(THREAD != _owner){if(THREAD->is_lock_owned((address) _owner)){// // Transmute _owner from a BasicLock pointer to a Thread address.// We don't need to hold _mutex for this transition.// Non-null to Non-null is safe as long as all readers can// tolerate either flavor.assert(_recursions ==0,"invariant");
_owner = THREAD ;
_recursions =0;OwnerIsThread=1;}else{// NOTE: we need to handle unbalanced monitor enter/exit// in native code by throwing an exception.// TODO: Throw an IllegalMonitorStateException ?
TEVENT (Exit-Throw IMSX);assert(false,"Non-balanced monitor enter/exit!");if(false){
THROW(vmSymbols::java_lang_IllegalMonitorStateException());}return;}}// 如果_recursions次数不为0.自减if(_recursions !=0){
_recursions--;// this is simple recursive enter
TEVENT (Inflatedexit- recursive);return;}//省略部分代码,根据不同的策略(由QMode指定),从cxq或EntryList中获取头节点,通过ObjectMonitor::ExitEpilog方法唤醒该节点封装的线程,唤醒操作最终由unpark完成。
除了enter和exit方法以外,objectMonitor.cpp中还有
void wait(jlong millis,bool interruptable, TRAPS);void notify(TRAPS);void notifyAll(TRAPS);
等方法。
总结
上面介绍的就是HotSpot虚拟机中Moniter的的加锁以及解锁的原理。
通过这篇文章我们知道了sychronized
加锁的时候,会调用objectMonitor的enter
方法,解锁的时候会调用exit
方法。事实上,只有在JDK1.6之前,synchronized
的实现才会直接调用ObjectMonitor的enter
和exit
,这种锁被称之为重量级锁。为什么说这种方式操作锁很重呢?
- Java的线程是映射到操作系统原生线程之上的,如果要阻塞或唤醒一个线程就需要操作系统的帮忙,这就要从用户态转换到核心态,因此状态转换需要花费很多的处理器时间,对于代码简单的同步块(如被
synchronized
修饰的get
或set
方法)状态转换消耗的时间有可能比用户代码执行的时间还要长,所以说synchronized
是java语言中一个重量级的操纵。
所以,在JDK1.6中出现对锁进行了很多的优化,进而出现轻量级锁,偏向锁,锁消除,适应性自旋锁,锁粗化(自旋锁在1.4就有 只不过默认的是关闭的,jdk1.6是默认开启的),这些操作都是为了在线程之间更高效的共享数据 ,解决竞争问题。
查看带有Synchronized语句块的class文件可以看到在同步代码块的起始位置插入了moniterenter
指令,在同步代码块结束的位置插入了monitorexit
指令。(JVM需要保证每一个monitorenter都有一个monitorexit与之相对应,但每个monitorexit不一定都有一个monitorenter)
但是查看同步方法的class文件时,同步方法并没有通过指令monitorenter和monitorexit来完成,而被翻译成普通的方法调用和返回指令,只是在其常量池中多了ACC_SYNCHRONIZED标示符。JVM就是根据该标示符来实现方法的同步的:当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。 其实本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需通过字节码来完成。
moniterenter和moniterexit指令是通过monitor对象实现的。
Synchronized的实现不仅与monitor对象有关,还与另一个东西密切相关,那就是对象头。
每个对象都有一个监视器锁(monitor)与之对应。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:
1、如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。2、如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1.
3.如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。
相关推荐
Host-Moniter是一款专用于网络设备监控的软件,它提供了全面的机房管理功能,能够帮助用户实时了解网络运行状态,及时发现并处理潜在问题,确保...用户通过深入理解和熟练运用,能够更好地管理和维护自己的网络环境。
可借鉴此优质项目实现复刻,也可基于此项目来扩展开发出更多功能 #注 1. 积分资源不提供技术指导/答疑 2. 本资源仅用于开源学习和技术交流。不可商用等,一切后果由使用者承担 3. 部分字体及插图等来自网络,若是...
GitHub敏感信息监控脚本,通过检索GitHub的搜索结果关键字来发现敏感信息,降低程序员GitHub项目上泄露敏感信息的可能性。
通过深入理解和有效使用这款工具,IT专业人士可以在日常工作中提升问题定位和处理的效率,保障系统的稳定运行。同时,了解"www.pudn.com.txt"和"File Moniter4.34"这两个文件的具体内容,将有助于用户更好地理解和...
开发者需要对QT框架有深入理解,同时熟悉视频编码和解码的基本原理,才能构建出高效、稳定且用户体验良好的视频监控回放系统。在这个过程中,"video_moniter1.rar"可能包含了实现该功能的源代码、资源文件或其他相关...
本文将深入探讨SQL_monitor的功能、工作原理以及如何有效利用它提升开发效率。 一、SQL_monitor的核心功能 1. SQL查询追踪:SQL_monitor能够记录并显示所有的SQL语句,包括INSERT、UPDATE、DELETE和SELECT等,这...
源代码的提供使得用户可以深入理解工具的工作原理,进行定制化修改或学习软件开发技巧。 标签"File Moniter4.34.rar源代码"和"PORTMON"进一步确认了这是PORTMON工具的源代码,存储在一个RAR压缩文件里。RAR是一种...
Baby Moniter的应用简易程序,频率是40MHz
测试驱动的小软件,很好用的。
source moniter代码检查 度量
### webservice10 使用moniter监视器 #### 知识点概述 - **WebService与Axis2**: WebService是一种跨编程语言、操作系统平台的远程调用技术。Axis2是Apache的一个项目,它是一个高性能、轻量级的Web服务框架,支持...
齐拉 QIRA是strace和gdb的竞争对手 有关高级用法的信息,请参见 所有QIRA代码均在MIT许可下发布 此仓库中的其他代码已根据各自的许可发布 支持的操作系统 Ubuntu 14.04 and 16.04 supported out of the box. ...
《Keil C51 Monitor 仿真...通过理解其设计原理和要点,开发者可以更好地利用这一工具,提高软件调试的效率和质量。在实际操作中,还需要结合硬件知识、信号处理技术以及电磁兼容性等因素,以确保仿真器的稳定和可靠。
数据监控,可以分析空中数据帧,反应出设备运行情况
交通视频监测程序是一种重要的智能交通管理系统,它利用先进的计算机视觉技术、图像处理和人工智能算法,对交通...研究和理解"Moniter-master"中的代码,可以帮助我们深入掌握智能交通监控系统的实现原理和技术细节。
《免费串口监控调试工具详解》 在计算机通信领域,串口(Serial Port)作为一种古老但仍然广泛应用的接口,常用于设备之间的数据传输。...通过深入理解和熟练运用,你可以在串口调试的世界里游刃有余。
在Java编程语言中,`Monitor`是一个重要的概念,它与多线程同步密切相关。`Monitor`通常是由一个或多个...通过学习和理解这些示例,开发者能够更好地掌握这些同步原语的使用,从而编写出更健壮、高效的多线程Java程序。
总的来说,Kafka应用监控是一项复杂而重要的任务,需要结合多种工具和技术,对Kafka集群的多个层面进行深入监控,从而确保系统的健壮性和高可用性。理解并掌握这些监控手段和指标,对于维护一个高效、可靠的Kafka...
通过对Ceph进程间通信机制的研究,我们不仅可以深入了解Ceph内部的工作原理,还可以学习到如何在分布式系统中高效地进行进程间通信。这对于构建高可用、高性能的分布式存储系统具有重要的参考价值。
Procmon是由Sysinternals Suite的一部分,它提供了深入的系统级洞察力,帮助用户诊断和解决各种系统问题。** Procmon的工作原理: ------------------ Procmon能够监控以下关键事件: 1. **进程创建与退出**: ...