java中synchronized关键字的实现

来源:互联网 发布:mysql数据库的触发器 编辑:程序博客网 时间:2024/06/04 20:45
java中synchronized关键字的实现说明:java中的同步(synchronized)是基于进入monitor对象和退出monitor对象来实现的,无论是显式同步还是隐式同步。synchronized语句块:1)代码编译后,从生成的class文件中可以看到:同步块的入口位置和出口位置(方法结束处和异常处)分别插入了monitorenter字节码指令和monitorexit字节码指令,故同步代码块属于显示同步。2)线程执行到monitorenter指令时,尝试获取对象的锁。synchronized方法:1)JVM从Class文件中的方法结构(method_info)中的 ACC_SYNCHRONIZED 访问标志区分一个方法是否为同步方法,故同步方法属于隐式同步。2)当方法调用时,调用指令会检查方法的ACC_SYNCHRONIZED访问标志是否被设置,如果设置了,执行线程将先持有monitor,然后再执行方法,最后在方法完成时释放monitor。3)在方法执行期间,其它线程无法获取该monitor。4)如果一个同步方法执行期间抛出了异常,并且在方法内部无法处理此异常,那这个同步方法所持有的monitor将在异常抛到同步方法之外时自动释放。method_info 结构格式如下(java虚拟机规范中的摘录):method_info {u2 access_flags;// 用于定义当前方法的访问权限和基本属性的标志u2 name_index;u2 descriptor_index;u2 attributes_count;attribute_info attributes[attributes_count];}method_info结构中访问标记(access_flags)的取值:标记名值说明ACC_PUBLIC0x0001public,方法可以从包外访问ACC_PRIVATE0x0002  private,方法只能本类中访问ACC_PROTECTED0x0004  protected,方法在自身和子类可以访问ACC_STATIC0x0008  static,静态方法ACC_FINAL0x0010  final,方法不能被重写ACC_SYNCHRONIZED0x0020  synchronized,方法由monitor同步...获取锁和释放锁的内存原语:当线程获取锁时,JMM会把该线程对应的本地内存置为无效。从而使得被monitor保护的临界区代码必须从主内存中读取共享变量。当线程释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中即:1将本地内存中的数据设置为无效,2从主内存中将数据复制到本地内存中,3在本地内存中进行操作,4操作完成后将本地内存中的数据刷新到主内存中。整体看起来就像是直接在主内存中操作一样。synchronized的可重入性:1)当一个线程再次请求自己持有对象锁的临界资源时,这种情况属于重入锁,请求将会成功。2)由于synchronized是基于monitor实现的,故每次重入,monitor中的计数器仍会加1。深入理解monitor:1)从java的视角看synchronizedsynchronized使用的锁对象(monitor)存储在java对象头中。monitor对象:1)每个java对象都拥有一个Monitor锁(别问我为什么,虚拟机就是这样设计的)。2)当一个monitor被持有后,它将处于锁定状态。2)从C++源码(虚拟机的实现)看synchronized (参考:https://www.cnblogs.com/dennyzhangdd/p/6734638.html) oopDesc ---> markOopDesc monitor()方法 --> ObjectMonitor 即 monitor -> monitor enter、monitor exit1)openjdk\hotspot\src\share\vm\oops\oop.hpp下的oopDesc类是JVM对象的顶级基类,故每个object都包含markOop。class oopDesc {friend class VMStructs;private:volatile markOop _mark;//标记字段(Mark Word)union _metadata {Klass*      _klass;//对象类型元数据的指针narrowKlass _compressed_klass;} _metadata;// Fast access to barrier set.  Must be initialized.static BarrierSet* _bs;public:markOop  mark() const         { return _mark; }markOop* mark_addr() const    { return (markOop*) &_mark; }void set_mark(volatile markOop m)      { _mark = m;   }void    release_set_mark(markOop m);markOop cas_set_mark(markOop new_mark, markOop old_mark);// Used only to re-initialize the mark word (e.g., of promoted// objects during a GC) -- requires a valid klass pointervoid init_mark();Klass* klass() const;Klass* klass_or_null() const volatile;Klass** klass_addr();narrowKlass* compressed_klass_addr();....省略...}2)markOopDesc继承自oopDesc,并拓展了自己的方法monitor(),该方法返回一个ObjectMonitor*对象指针。ObjectMonitor* monitor() const {assert(has_monitor(), "check");// Use xor instead of &~ to provide one extra tag-bit check.return (ObjectMonitor*) (value() ^ monitor_value);//-------------- 补充 ---------------------//value()的实现: uintptr_t value() const { return (uintptr_t) this; }////monitor_value的实现://enum {//locked_value             = 0,//00偏向锁 //unlocked_value           = 1,//01无锁//monitor_value            = 2,//10监视器锁,又叫重量级锁//marked_value             = 3,//11GC标记//biased_lock_pattern      = 5 //101偏向锁//};//-------------- 补充 ---------------------}3)在HotSpot虚拟机中,采用ObjectMonitor类来实现monitor。openjdk\hotspot\src\share\vm\runtime\objectMonitor.hpp源码如下:ObjectMonitor() {_header       = NULL;//markOop对象头_count        = 0;_waiters      = 0,//等待线程数_recursions   = 0;//重入次数_object       = NULL;_owner        = NULL;//指向获得ObjectMonitor对象的线程或基础锁_WaitSet      = NULL;//处于wait状态的线程,会被加入到wait set;_WaitSetLock  = 0 ;_Responsible  = NULL ;_succ         = NULL ;_cxq          = NULL ;FreeNext      = NULL ;_EntryList    = NULL ;//处于等待锁block状态的线程,会被加入到entry set;_SpinFreq     = 0 ;_SpinClock    = 0 ;OwnerIsThread = 0 ;// _owner is (Thread *) vs SP/BasicLock_previous_owner_tid = 0;// 监视器前一个拥有者线程的ID}

原创粉丝点击