Synchronized的原理(C++层)

Synchronized的原理(C++层)

原理描述:

重量级锁通过对象内部的监视器(monitor)实现,其中monitor的本质是依赖于底层操作系统的Mutex Lock实现。当系统检查到锁是重量级锁之后,会把等待想要获得锁的线程进行阻塞,被阻塞的线程不会消耗cup。但是阻塞或者唤醒一个线程时,都需要操作系统来帮忙,这就需要从用户态转换到内核态,而转换状态是需要消耗很多时间的,有可能比用户执行代码的时间还要长。这就是说为什么重量级线程开销成本很高。

无论是ACC_SYNCHRONIZED(方法锁)还是monitorenter、monitorexit都是基于Monitor实现的,在Java虚拟机(HotSpot)中,Monitor是基于C++实现的,由ObjectMonitor类实现。

ObjectMonitor类中提供了几个方法,如enter、exit、wait、notify、notifyAll等。sychronized加锁的时候,会调用objectMonitor的enter方法,解锁的时候会调用exit方法。

monitor(管程/监视器)

我们可以把Monitor理解为一个同步工具,也可以描述为一种同步机制。所有的Java对象是天生的Monitor,每个object的对象里markOop->monitor() 里可以保存ObjectMonitor的对象。

 

要了解monitor需要具备几个知识点

1.java对象头中的与锁有关的区域为Mark Word(详细上边看java对象模型章节)

1.Mark Word中lock标志位(2bit) 分别对应无锁(01)、偏向锁(01)、轻量级锁(00)、重量级锁(10)

2.无锁(01)、偏向锁(01)由1bit的位置标示是否为偏向锁, 无锁(0), 偏向锁(1)

3.偏向锁用不着monitor, Mark Word中存放的是线程ID, 用的线程ID判断.

4.轻量级锁用不着monitor,Mark Word中存放是指向当前线程的栈帧中创建锁记录空间的指针

5.重量级锁是基于monitor实现的, 这时Mark Word中存放的就是指向monitor对象的指针

6.synchronized在1.5之前只有重量级锁, 也称为传统锁. 1.6才引入偏向锁和轻量级锁

 

C++源码:

oop.hpp下的oopDesc类是JVM对象的顶级基类,所以每个object对象都包含markOop

oop.hpp,每个 Java Object 在 JVM 内部都有一个 native 的 C++ 对象 oop/oopDesc 与之对应。先在oop.hpp中看oopDesc的定义:

class oopDesc {//顶层基类
  friend class VMStructs;
 private:
  volatile markOop  _mark;//理解为对象头也就是每个对象的mark work头
  union _metadata {
    Klass*      _klass;
    narrowKlass _compressed_klass;
  } _metadata;
......

_mark 被声明在 oopDesc 类的顶部,所以这个 _mark 可以认为是一个 头部, 也就是上面那个图种提到的头部保存了一些重要的状态和标识信息,在markOop.hpp文件中有一些注释说明markOop的内存布局:

markOop.hpp 中 markOopDesc继承自oopDesc,

class BasicLock;
class objectMonitor;
class JavaThread;
class markOopDesc: public oopDesc {
    private:
        // Conversion
        uintptr_ t value() const { return (uintptr_ _t) this; }
并扩展了自己的monitor方法,这个方法返回一个ObjectMonitor指针对象:这个ObjectMonitor 其实就是对象监视器
objectMonitor* monitor() const {
    assert(has_monitor(), "check");
    // Use xor instead of &~ to provide one extra tag-bit check.
    return (ObjectMonitor*) (value() ^ monitor_value );
}

objectMonitor.hpp, 在hotspot虚拟机中,采用ObjectMonitor类来实现monitor:

ObjectMonitor() {
    _header       = NULL; //markOop对象头
    _count        = 0; //获取锁的次数(释放会-1)(**重点**)
    _waiters      = 0, //等待线程数
    _recursions   = 0; //锁的重入次数(释放不会-1)
    _object       = NULL;
    _owner        = NULL; //指向获得ObjectMonitor对象的线程(**重点**)
    _WaitSet      = NULL; //处于wait状态的线程, 会被加入waitSet(**重点**)
    _WaitSetLock  = 0 ;
    _Responsible  = NULL ;
    _succ         = NULL ;
    _cxq          = NULL ; //JVM 为每个尝试进入synchronized代码段的Java线程创建一个ObjectWaiter并添加到_cxq队列中
    FreeNext      = NULL ;
    _EntryList    = NULL ; //处于等待锁block状态的线程, 由ObjectWaiter组成双向链表, JVM会从该链表中取出一个ObjectWaiter并唤醒对应的java线程(**重点**)
    _SpinFreq     = 0 ;
    _SpinClock    = 0 ;
    OwnerIsThread = 0 ;
    _previous_owner_tid = 0; //监视器钱一个拥有者的线程id
  }

ObjectMonitor类重要方法

获得锁enter

void ATTR ObjectMonitor::enter(TRAPS) {
  Thread * const Self = 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 == 1
     return ;
  }
//如果旧值和当前线程一样,说明当前线程已经持有锁,此次为重入,_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();
}
}

 

释放锁exit

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 (Inflated exit - recursive) ;
     return ;
   }

省略部分代码,根据不同的策略(由QMode指定),从cxq或EntryList中获取头节点,通过ObjectMonitor::ExitEpilog方法唤醒该节点封装的线程,唤醒操作最终由unpark完成。

 

等待唤醒wait

封装 ObjectWaiter 对象并将其放入 _WaitSet 队列,并调用 park()将线程挂起。

// Wait/Notify/NotifyAll
// Note: a subset of changes to ObjectMonitor::wait()
// will need to be replicated in complete_exit above
void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {
   Thread * const Self = THREAD ;
   assert(Self->is_Java_thread(), "Must be Java thread!");
   JavaThread *jt = (JavaThread *)THREAD;
   DeferredInitialize () ;
   // Throw IMSX or IEX.
   CHECK_OWNER();//检查objectMonitor对象是否指向本线程(即是否获得锁)
   // ... 省略中间的代码
​
   // create a node to be put into the queue
   // Critically, after we reset() the event but prior to park(), we must check
   // for a pending interrupt.
   // 封装了一个ObjectWaiter对象
   ObjectWaiter node(Self);
   node.TState = ObjectWaiter::TS_WAIT ;
   Self->_ParkEvent->reset() ;
   OrderAccess::fence();//内存屏障          // ST into Event; membar ; LD interrupted-flag
   // Enter the waiting queue, which is a circular doubly linked list in this case
   // but it could be a priority queue or any data structure.
   // _WaitSetLock protects the wait queue.  Normally the wait queue is accessed only
   // by the the owner of the monitor *except* in the case where park()
   // returns because of a timeout of interrupt.  Contention is exceptionally rare
   // so we use a simple spin-lock instead of a heavier-weight blocking lock.
   //将ObjectWaiter放入 _WaitSet中
   Thread::SpinAcquire (&_WaitSetLock, "WaitSet - add") ;
   AddWaiter (&node) ;
   Thread::SpinRelease (&_WaitSetLock) ;
   if ((SyncFlags & 4) == 0) {
      _Responsible = NULL ;
   }
   intptr_t save = _recursions; // record the old recursion count
   _waiters++;                  // increment the number of waiters
   _recursions = 0;             // set the recursion level to be 1
   exit (true, Self) ;                    // exit the monitor
   guarantee (_owner != Self, "invariant") ;
   //.....省略中间代码
   // The thread is on the WaitSet list - now park() it.
   // On MP systems it's conceivable that a brief spin before we park
   // could be profitable.
   // TODO-FIXME: change the following logic to a loop of the form
   //   while (!timeout && !interrupted && _notified == 0) park()
   int ret = OS_OK ;
   int WasNotified = 0 ;
   { // State transition wrappers
     OSThread* osthread = Self->osthread();
     OSThreadWaitState osts(osthread, true);
     {
       ThreadBlockInVM tbivm(jt);
       // Thread is in thread_blocked state and oop access is unsafe.
       jt->set_suspend_equivalent();
       if (interruptible && (Thread::is_interrupted(THREAD, false) || HAS_PENDING_EXCEPTION)) {
           // Intentionally empty
       } else
       if (node._notified == 0) {
         if (millis <= 0) { // 调用park()将线程挂起
            Self->_ParkEvent->park () ;
         } else {
            ret = Self->_ParkEvent->park (millis) ;
         }
       }
    .......
}

通知唤醒notify

void ObjectMonitor::notify(TRAPS) {
  CHECK_OWNER();//同样先检查objectMonitor对象是否指向本线程
  if (_WaitSet == NULL) {//判断wait队列是否为空
     TEVENT (Empty-Notify) ;
     return ;
  }
  DTRACE_MONITOR_PROBE(notify, this, object(), THREAD);
​
  int Policy = Knob_MoveNotifyee ;
  // 这个 WaitSet - notify 很了然 
  Thread::SpinAcquire (&_WaitSetLock, "WaitSet - notify") ;
  ObjectWaiter * iterator = DequeueWaiter() ;//dequeue _WaitSet 队列
  if (iterator != NULL) {//不为空,然后接下去就是一系列的判断,最后去唤醒
     //.......
  }
}

 

[synchronized原理及优化] https://www.jianshu.com/p/435c20a64da1

[synchronized原理] https://www.cnblogs.com/wuzhenzhao/p/10250801.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值