对jvm 同步锁的理解

sychronized是jvm中对线程同步的主要方法和机制。

Thread.sleep是让线程丢掉cpu分配资源(让调度器在某时间内不再调度该线程)。

一个对象的锁只能同时被一个线程所持有。

对于每一个线程都是两个ObjectMinor list,一个是freelist,一个是usedlist;usedlist是该线程占有那些对象的锁,一个线程可以占有多个对象的锁。

jvm ObjectMinor源代码:

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
  }

object notify() 随意获取(之前调用wait而阻塞的线程)一个线程唤醒去执行,但是去执行不一定是直接获取这个对象的锁,或者执行这个对象中方法,只是唤醒而已。但是注意:去执行object.notify的线程必须是获取该对象object的锁的线程,否则会爆出IllegalMonitorStateException。因为只有持有该对象的锁,该线程中的minor usedlist才有该对象的objectminor,才有能力获取该objectminor中的_WaitSet 等待队列。

对于ObjectMinor中EntrySet存放的阻塞block队列,我们是没有办法直接唤醒的.

每个object都包含markOop。如下图所示:

class oopDesc {
    friend class VMStructs;
   private:
    volatile markOop  _mark;//markOop: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 pointer
   void init_mark();
 
   Klass* klass() const;
   Klass* klass_or_null() const volatile;
   Klass** klass_addr();
   narrowKlass* compressed_klass_addr();

}
oopDesc --继承--> markOopDesc --方法monitor()--> ObjectMonitor-->enter、exit 获取、释放锁

markOopDesc类

openjdk\hotspot\src\share\vm\oops\markOop.hpp下markOopDesc继承自oopDesc,并拓展了自己的方法monitor(),如下图

ObjectMonitor* monitor() const {
     assert(has_monitor(), "check");
    // Use xor instead of &~ to provide one extra tag-bit check.
     return (ObjectMonitor*) (value() ^ monitor_value);
   }

说说EntrySet和waitSet队列:

对jvm 同步锁的理解_第1张图片

(1)所有期待获得锁的线程,在锁已经被其它线程拥有的时候,这些期待获得锁的线程就进入了Object Lock的entry set区域。

(2)所有曾经获得过锁,但是由于其它必要条件不满足而需要wait的时候,线程就进入了Object Lock的wait set区域 。

(3)在wait set区域的线程获得Notify/notifyAll通知的时候,随机的一个Thread(Notify)或者是全部的Thread(NotifyALL)从Object Lock的wait set区域进入了entry set中。

(4)在当前拥有锁的线程释放掉锁的时候,处于该Object Lock的entryset区域的线程都会抢占该锁,但是只能有任意的一个Thread能取得该锁,而其他线程依然在entry set中等待下次来抢占到锁之后再执行。

当获取当前锁的线程离开同步区,对象的锁值减一,且锁值为0(锁可以重复进入,每次进入自加1),那么监视器会释放信息量,Entry_set队列中的线程会抢锁,只有一个线程可以获取锁。



你可能感兴趣的:(对jvm 同步锁的理解)