深入剖析Java關(guān)鍵字之synchronized(源碼篇)

一、摘要

?在《深入剖析Java關(guān)鍵字之synchronized(原理篇)》中球化,我們從使用和原理上面分析了synchronized關(guān)鍵字众旗,我們知道夕土,synchronized是通過monitor監(jiān)視器鎖來實現(xiàn)埠戳;這一篇我們將更多的從字節(jié)碼以及JVM的源碼層面來分析synchronized關(guān)鍵字的實現(xiàn)井誉。


二、synchronized的字節(jié)碼分析

?在《Java class文件結(jié)構(gòu)(規(guī)范篇)》中我們知道由synchronized修飾的方法編譯成字節(jié)碼的時候方法的access_flags會有ACC_SYNCHRONIZED標識整胃,我們把《深入剖析Java關(guān)鍵字之synchronized(原理篇)》中SynchronizedMethod的class文件使用javap -v SynchronizedMethod.class命令來查看之后如下:


?在方法的描述上面果然有ACC_SYNCHRONIZED颗圣;那么,如果反編譯代碼塊屁使,會得到什么結(jié)果呢在岂?我們使用javap -v SynchronizedCodeBlock.class命令來查看SynchronizedCodeBlock的字節(jié)碼,如下:

?我們可以看到increase方法中有一個monitorenter和兩個monitorexit蛮寂;無論ACC_SYNCHRONIZED模式還是monitorenter蔽午、monitorexit模式,本質(zhì)上都是對一個對象的監(jiān)視器(monitor)進行獲取酬蹋,而這個獲取的過程是排他的及老,也就是同一個時刻只能有一個線程獲得同步塊對象的監(jiān)視器, ACC_SYNCHRONIZED只是通過隱式的方式實現(xiàn)范抓。在 《深入剖析Java關(guān)鍵字之synchronized(原理篇)》文章中骄恶,有提到對象監(jiān)視器:

synchronized關(guān)鍵字經(jīng)過編譯之后,會在同步塊的前后分別形成monitorenter和monitorexit這兩個字節(jié)碼指令尉咕。當我們的JVM把字節(jié)碼加載到內(nèi)存的時候叠蝇,會對這兩個指令進行解析。這兩個字節(jié)碼都需要一個Object類型的參數(shù)來指明要鎖定和解鎖的對象年缎。如果Java程序中的synchronized明確指定了對象參數(shù)悔捶,那么這個對象就是加鎖和解鎖的對象;如果沒有明確指定单芜,那就根據(jù)synchronized修飾的是實例方法還是類方法蜕该,獲取對應(yīng)的對象實例或Class對象來作為鎖對象

?接下來,我們來看下JVM規(guī)范中關(guān)于同步的描述:

?Java虛擬機可以支持方法級的同步和方法內(nèi)一段指令序列的同步洲鸠,這兩種同步結(jié)構(gòu)都是使用同步鎖(monitor)來支持的堂淡。
?方法級的同步是隱式的,即無需通過字節(jié)碼指令來控制扒腕,它實現(xiàn)在方法調(diào)用和返回操作之中绢淀。虛擬機可以從方法常量池中的方法表結(jié)構(gòu)(method_info structure)中的 ACC_SYNCHRONIZED訪問標志是否設(shè)置,如果設(shè)置了瘾腰,執(zhí)行線程將先持有同步鎖皆的,然后執(zhí)行方法,最后在方法完成(無論是正常完成還是非正常完成)時釋放同步鎖蹋盆。在方法執(zhí)行期間费薄,執(zhí)行線程持有了同步鎖硝全,其他任何線程都無法再獲得同一個鎖。如果一個同步方法執(zhí)行期間拋出了異常楞抡,并且在方法內(nèi)部無法處理此異常伟众,那這個同步方法所持有的鎖將在異常拋到同步方法之外時自動釋放。
?指令序列的同步通常用來表示Java語言中的synchronized塊召廷,Java虛擬機的指令集中有monitorenter和monitorexit兩個指令來支持這種synchronized關(guān)鍵字的語義凳厢。正確實現(xiàn)synchronized關(guān)鍵字需要編譯器與Java虛擬機兩者協(xié)作支持。
?結(jié)構(gòu)化鎖定(structure locking)是指在方法調(diào)用期間每一個同步鎖退出都與前面的同步鎖進入相匹配的情形竞慢。因為無法保證所有提交給Java虛擬機執(zhí)行的代碼都滿足結(jié)構(gòu)化鎖定数初,所以允許Java虛擬機允許(但不強制要求)通過以下兩條規(guī)則來保證結(jié)構(gòu)化鎖定成立。假設(shè)T代表一個線程梗顺,M代表一個同步鎖,那么:
1. T在方法執(zhí)行時持有同步鎖M的次數(shù)必須與T在方法執(zhí)行(包括正常和非正常完成)時釋放同步鎖M的次數(shù)相等车摄。
2. 在方法調(diào)用過程中寺谤,任何時刻都不會出現(xiàn)線程T釋放同步鎖M的次數(shù)比T持有同步鎖M次數(shù)多的情況。
?請注意吮播,在調(diào)用同步方法時也認為自動持有和釋放鎖的過程是在方法調(diào)用期間發(fā)生变屁。

?從JVM規(guī)范中,我們也可以看出來Java的同步機制是通過monitor來實現(xiàn)的意狠,那么什么是monitor呢粟关?JVM源碼又是怎么實現(xiàn)的呢?


三环戈、monitor源碼分析

?在分析monitor的源碼之前闷板,我們需要了解oopoopDesc院塞,markOop等相關(guān)概念遮晚,在《深入剖析Java關(guān)鍵字之synchronized(原理篇)》這篇文章中,我們知道了synchronized的同步鎖實際上是存儲在對象頭中拦止,這個對象頭是一個Java對象在內(nèi)存中布局的一部分县遣。Java中的每一個Object在JVM內(nèi)部都會有一個native的C++對象oop/oopDesc與之對應(yīng)。在hotspot源碼 oop.hppoopDesc的定義如下:

class oopDesc {
  friend class VMStructs;
  friend class JVMCIVMStructs;
 private:
  volatile markOop _mark;
  union _metadata {
    Klass*      _klass;
    narrowKlass _compressed_klass;
  } _metadata;

?其中 markOop就是我們所說的Mark Word汹族,用于存儲鎖的標識萧求。 hotspot源碼 markOop.hpp文件代碼片段:

class markOopDesc: public oopDesc {
 private:
  // Conversion
  uintptr_t value() const { return (uintptr_t) this; }

 public:
  // Constants
  enum { age_bits                 = 4,
         lock_bits                = 2,
         biased_lock_bits         = 1,
         max_hash_bits            = BitsPerWord - age_bits - lock_bits - biased_lock_bits,
         hash_bits                = max_hash_bits > 31 ? 31 : max_hash_bits,
         cms_bits                 = LP64_ONLY(1) NOT_LP64(0),
         epoch_bits               = 2
  };
......
}

?markOopDesc繼承自oopDesc,并且擴展了自己的monitor方法顶瞒,這個方法返回一個ObjectMonitor指針對象夸政,在hotspot虛擬機中,采用ObjectMonitor類來實現(xiàn)monitor

bool has_monitor() const {
    return ((value() & monitor_value) != 0);
  }
  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中搁拙,可以看到ObjectMonitor的定義:

class ObjectMonitor {
 public:
  enum {
    OM_OK,                    // no error
    OM_SYSTEM_ERROR,          // operating system error
    OM_ILLEGAL_MONITOR_STATE, // IllegalMonitorStateException
    OM_INTERRUPTED,           // Thread.interrupt()
    OM_TIMED_OUT              // Object.wait() timed out
  };

 private:
  friend class ObjectSynchronizer;
  friend class ObjectWaiter;
  friend class VMStructs;

  volatile markOop   _header;       // displaced object header word - mark
  void*     volatile _object;       // backward object pointer - strong root
 public:
  ObjectMonitor*     FreeNext;      // Free list linkage
 private:
  DEFINE_PAD_MINUS_SIZE(0, DEFAULT_CACHE_LINE_SIZE,
                        sizeof(volatile markOop) + sizeof(void * volatile) +
                        sizeof(ObjectMonitor *));
 protected:                         // protected for JvmtiRawMonitor
  void *  volatile _owner;          // pointer to owning thread OR BasicLock
  volatile jlong _previous_owner_tid;  // thread id of the previous owner of the monitor
  volatile intptr_t  _recursions;   // recursion count, 0 for first entry
  ObjectWaiter * volatile _EntryList; // Threads blocked on entry or reentry.
                                      // The list is actually composed of WaitNodes,
                                      // acting as proxies for Threads.
 private:
  ObjectWaiter * volatile _cxq;     // LL of recently-arrived threads blocked on entry.
  Thread * volatile _succ;          // Heir presumptive thread - used for futile wakeup throttling
  Thread * volatile _Responsible;

  volatile int _Spinner;            // for exit->spinner handoff optimization
  volatile int _SpinDuration;

  volatile jint  _count;            // reference count to prevent reclamation/deflation
                                    // at stop-the-world time.  See deflate_idle_monitors().
                                    // _count is approximately |_WaitSet| + |_EntryList|
 protected:
  ObjectWaiter * volatile _WaitSet; // LL of threads wait()ing on the monitor
  volatile jint  _waiters;          // number of waiting threads
 private:
  volatile int _WaitSetLock;        // protects Wait Queue - simple spinlock

 public:
  static void Initialize();

  // Only perform a PerfData operation if the PerfData object has been
  // allocated and if the PerfDataManager has not freed the PerfData
  // objects which can happen at normal VM shutdown.
  //
  #define OM_PERFDATA_OP(f, op_str)              \
    do {                                         \
      if (ObjectMonitor::_sync_ ## f != NULL &&  \
          PerfDataManager::has_PerfData()) {     \
        ObjectMonitor::_sync_ ## f->op_str;      \
      }                                          \
    } while (0)

  static PerfCounter * _sync_ContendedLockAttempts;
  static PerfCounter * _sync_FutileWakeups;
  static PerfCounter * _sync_Parks;
  static PerfCounter * _sync_Notifications;
  static PerfCounter * _sync_Inflations;
  static PerfCounter * _sync_Deflations;
  static PerfLongVariable * _sync_MonExtant;

  static int Knob_ExitRelease;
  static int Knob_InlineNotify;
  static int Knob_Verbose;
  static int Knob_VerifyInUse;
  static int Knob_VerifyMatch;
  static int Knob_SpinLimit;

  void* operator new (size_t size) throw();
  void* operator new[] (size_t size) throw();
  void operator delete(void* p);
  void operator delete[] (void *p);

  // TODO-FIXME: the "offset" routines should return a type of off_t instead of int ...
  // ByteSize would also be an appropriate type.
  static int header_offset_in_bytes()      { return offset_of(ObjectMonitor, _header); }
  static int object_offset_in_bytes()      { return offset_of(ObjectMonitor, _object); }
  static int owner_offset_in_bytes()       { return offset_of(ObjectMonitor, _owner); }
  static int count_offset_in_bytes()       { return offset_of(ObjectMonitor, _count); }
  static int recursions_offset_in_bytes()  { return offset_of(ObjectMonitor, _recursions); }
  static int cxq_offset_in_bytes()         { return offset_of(ObjectMonitor, _cxq); }
  static int succ_offset_in_bytes()        { return offset_of(ObjectMonitor, _succ); }
  static int EntryList_offset_in_bytes()   { return offset_of(ObjectMonitor, _EntryList); }

  #define OM_OFFSET_NO_MONITOR_VALUE_TAG(f) \
    ((ObjectMonitor::f ## _offset_in_bytes()) - markOopDesc::monitor_value)

  markOop   header() const;
  volatile markOop* header_addr();
  void      set_header(markOop hdr);

  intptr_t is_busy() const {
    // TODO-FIXME: merge _count and _waiters.
    // TODO-FIXME: assert _owner == null implies _recursions = 0
    // TODO-FIXME: assert _WaitSet != null implies _count > 0
    return _count|_waiters|intptr_t(_owner)|intptr_t(_cxq)|intptr_t(_EntryList);
  }

  intptr_t  is_entered(Thread* current) const;

  void*     owner() const;
  void      set_owner(void* owner);

  jint      waiters() const;

  jint      count() const;
  void      set_count(jint count);
  jint      contentions() const;
  intptr_t  recursions() const                                         { return _recursions; }

  // JVM/TI GetObjectMonitorUsage() needs this:
  ObjectWaiter* first_waiter()                                         { return _WaitSet; }
  ObjectWaiter* next_waiter(ObjectWaiter* o)                           { return o->_next; }
  Thread* thread_of_waiter(ObjectWaiter* o)                            { return o->_thread; }

 protected:
  // We don't typically expect or want the ctors or dtors to run.
  // normal ObjectMonitors are type-stable and immortal.
  ObjectMonitor() { ::memset((void *)this, 0, sizeof(*this)); }

  ~ObjectMonitor() {
    // TODO: Add asserts ...
    // _cxq == 0 _succ == NULL _owner == NULL _waiters == 0
    // _count == 0 _EntryList  == NULL etc
  }
......

#endif // SHARE_VM_RUNTIME_OBJECTMONITOR_HPP

?所以同步塊的實現(xiàn)使用 monitorenter和 monitorexit指令秒梳,而同步方法是依靠方法修飾符上的flag ACC_SYNCHRONIZED來完成法绵。其本質(zhì)是對一個對象監(jiān)視器(monitor)進行獲取,這個獲取過程是排他的酪碘,也就是同一個時刻只能有一個線程獲得由synchronized所保護對象的監(jiān)視器朋譬。所謂的監(jiān)視器,實際上可以理解為一個同步工具兴垦,它是由Java對象進行描述的徙赢。在Hotspot中,是通過ObjectMonitor來實現(xiàn)探越,每個對象中都會內(nèi)置一個ObjectMonitor對象狡赐。如圖所示:


ObjectMonitor

四、synchronized源碼分析

?從 monitorentermonitorexit這兩個指令來開始閱讀源碼钦幔,JVM將字節(jié)碼加載到內(nèi)存以后枕屉,會對這兩個指令進行解釋執(zhí)行, monitorenter, monitorexit的指令解析是通過 interpreterRuntime.cpp中的兩個方法實現(xiàn):

  // Synchronization
  static void    monitorenter(JavaThread* thread, BasicObjectLock* elem);
  static void    monitorexit (JavaThread* thread, BasicObjectLock* elem);
  //JavaThread 當前獲取鎖的線程
  //BasicObjectLock 基礎(chǔ)對象鎖

?我們基于monitorenter為入口鲤氢,沿著偏向鎖 -> 輕量級鎖 -> 重量級鎖的路徑來分析synchronized的實現(xiàn)過程搀擂,繼續(xù)看interpreterRuntime.cpp源碼:

//------------------------------------------------------------------------------------------------------------------------
// Synchronization
//
// The interpreter's synchronization code is factored out so that it can
// be shared by method invocation and synchronized blocks.
//%note synchronization_3

//%note monitor_1
IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* thread, BasicObjectLock* elem))
#ifdef ASSERT
  thread->last_frame().interpreter_frame_verify_monitor(elem);
#endif
  if (PrintBiasedLockingStatistics) {
    Atomic::inc(BiasedLocking::slow_path_entry_count_addr());
  }
  Handle h_obj(thread, elem->obj());
  assert(Universe::heap()->is_in_reserved_or_null(h_obj()),
         "must be NULL or an object");
  if (UseBiasedLocking) {
    // Retry fast entry if bias is revoked to avoid unnecessary inflation
    ObjectSynchronizer::fast_enter(h_obj, elem->lock(), true, CHECK);
  } else {
    ObjectSynchronizer::slow_enter(h_obj, elem->lock(), CHECK);
  }
  assert(Universe::heap()->is_in_reserved_or_null(elem->obj()),
         "must be NULL or an object");
#ifdef ASSERT
  thread->last_frame().interpreter_frame_verify_monitor(elem);
#endif
IRT_END

?UseBiasedLocking是在JVM啟動的時候,是否啟動偏向鎖的標識::

  • 如果支持偏向鎖,則執(zhí)行 ObjectSynchronizer::fast_enter的邏輯
  • 如果不支持偏向鎖,則執(zhí)行 ObjectSynchronizer::slow_enter邏輯卷玉,繞過偏向鎖哨颂,直接進入輕量級鎖

?ObjectSynchronizer::fast_enter的實現(xiàn)在 synchronizer.cpp文件中,代碼如下:

// -----------------------------------------------------------------------------
//  Fast Monitor Enter/Exit
// This the fast monitor enter. The interpreter and compiler use
// some assembly copies of this code. Make sure update those code
// if the following function is changed. The implementation is
// extremely sensitive to race condition. Be careful.

void ObjectSynchronizer::fast_enter(Handle obj, BasicLock* lock,
                                    bool attempt_rebias, TRAPS) {
  if (UseBiasedLocking) { //判斷是否開啟了偏向鎖
    if (!SafepointSynchronize::is_at_safepoint()) {  //如果不處于全局安全點
      //通過`revoke_and_rebias`這個函數(shù)嘗試獲取偏向鎖
      BiasedLocking::Condition cond = BiasedLocking::revoke_and_rebias(obj, attempt_rebias, THREAD);
      if (cond == BiasedLocking::BIAS_REVOKED_AND_REBIASED) { //如果是撤銷與重偏向直接返回
        return;
      }
    } else { //如果在安全點相种,撤銷偏向鎖
      assert(!attempt_rebias, "can not rebias toward VM thread");
      BiasedLocking::revoke_at_safepoint(obj);
    }
    assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now");
  }

  slow_enter(obj, lock, THREAD);
}

? fast_enter方法的主要流程做一個簡單的解釋:

  • 再次檢查偏向鎖是否開啟
  • 當處于不安全點時威恼,通過 revoke_and_rebias嘗試獲取偏向鎖,如果成功則直接返回寝并,如果失敗則進入輕量級鎖獲取過程
  • revoke_and_rebias這個偏向鎖的獲取邏輯在 biasedLocking.cpp
  • 如果偏向鎖未開啟箫措,則進入slow_enter獲取輕量級鎖的流程

偏向鎖的獲取

?BiasedLocking::revoke_and_rebias是用來獲取當前偏向鎖的狀態(tài)(可能是偏向鎖撤銷后重新偏向)。這個方法的邏輯在 biasedLocking.cpp

BiasedLocking::Condition BiasedLocking::revoke_and_rebias(Handle obj, bool attempt_rebias, TRAPS) {
  assert(!SafepointSynchronize::is_at_safepoint(), "must not be called while at safepoint");

  // We can revoke the biases of anonymously-biased objects
  // efficiently enough that we should not cause these revocations to
  // update the heuristics because doing so may cause unwanted bulk
  // revocations (which are expensive) to occur.
  markOop mark = obj->mark(); //獲取鎖對象的對象頭
  //判斷mark是否為可偏向狀態(tài)食茎,即mark的偏向鎖標志位為1蒂破,鎖標志位為 01,線程id為null
  if (mark->is_biased_anonymously() && !attempt_rebias) {
    // We are probably trying to revoke the bias of this object due to
    // an identity hash code computation. Try to revoke the bias
    // without a safepoint. This is possible if we can successfully
    // compare-and-exchange an unbiased header into the mark word of
    // the object, meaning that no other thread has raced to acquire
    // the bias of the object.
    //這個分支是進行對象的hashCode計算時會進入别渔,在一個非全局安全點進行偏向鎖撤銷
    markOop biased_value       = mark;
    //創(chuàng)建一個非偏向的markword
    markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age());
    markOop res_mark = obj->cas_set_mark(unbiased_prototype, mark);
    if (res_mark == biased_value) { //如果CAS成功附迷,返回偏向鎖撤銷狀態(tài)
      return BIAS_REVOKED;
    }
  } else if (mark->has_bias_pattern()) {//如果鎖對象為可偏向狀態(tài)(biased_lock:1, lock:01,不管線程id是否為空),嘗試重新偏向
    Klass* k = obj->klass();
    markOop prototype_header = k->prototype_header();
    if (!prototype_header->has_bias_pattern()) { //如果已經(jīng)有線程對鎖對象進行了全局鎖定哎媚,則取消偏向鎖操作
      // This object has a stale bias from before the bulk revocation
      // for this data type occurred. It's pointless to update the
      // heuristics at this point so simply update the header with a
      // CAS. If we fail this race, the object's bias has been revoked
      // by another thread so we simply return and let the caller deal
      // with it.
      markOop biased_value       = mark;
      //CAS 更新對象頭markword為非偏向鎖
      markOop res_mark = obj->cas_set_mark(prototype_header, mark);
      assert(!obj->mark()->has_bias_pattern(), "even if we raced, should still be revoked");
      return BIAS_REVOKED; //返回偏向鎖撤銷狀態(tài)
    } else if (prototype_header->bias_epoch() != mark->bias_epoch()) {
      // The epoch of this biasing has expired indicating that the
      // object is effectively unbiased. Depending on whether we need
      // to rebias or revoke the bias of this object we can do it
      // efficiently enough with a CAS that we shouldn't update the
      // heuristics. This is normally done in the assembly code but we
      // can reach this point due to various points in the runtime
      // needing to revoke biases.
      //如果偏向鎖過期喇伯,則進入當前分支
      if (attempt_rebias) {  //如果允許嘗試獲取偏向鎖
        assert(THREAD->is_Java_thread(), "");
        markOop biased_value       = mark;
        markOop rebiased_prototype = markOopDesc::encode((JavaThread*) THREAD, mark->age(), prototype_header->bias_epoch());
        //通過CAS 操作, 將本線程的 ThreadID 拨与、時間錯稻据、分代年齡嘗試寫入對象頭中
        markOop res_mark = obj->cas_set_mark(rebiased_prototype, mark);
        if (res_mark == biased_value) { //CAS成功,則返回撤銷和重新偏向狀態(tài)
          return BIAS_REVOKED_AND_REBIASED;
        }
      } else { //不嘗試獲取偏向鎖,則取消偏向鎖
        //通過CAS操作更新分代年齡
        markOop biased_value       = mark;
        markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age());
        markOop res_mark = obj->cas_set_mark(unbiased_prototype, mark);
        if (res_mark == biased_value) { //如果CAS操作成功捻悯,返回偏向鎖撤銷狀態(tài)
          return BIAS_REVOKED;
        }
      }
    }
  }

偏向鎖的撤銷

?當?shù)竭_一個全局安全點時匆赃,這時會根據(jù)偏向鎖的狀態(tài)來判斷是否需要撤銷偏向鎖,調(diào)用 revoke_at_safepoint方法今缚,這個方法也是在 biasedLocking.cpp中定義的:

void BiasedLocking::revoke_at_safepoint(Handle h_obj) {
  assert(SafepointSynchronize::is_at_safepoint(), "must only be called while at safepoint");
  oop obj = h_obj()
  //更新撤銷偏向鎖計數(shù)算柳,并返回偏向鎖撤銷次數(shù)和偏向次數(shù)
  HeuristicsResult heuristics = update_heuristics(obj, false);
  if (heuristics == HR_SINGLE_REVOKE) {  //可偏向且未達到批量處理的閾值(下面會單獨解釋)
    revoke_bias(obj, false, false, NULL, NULL);
  } else if ((heuristics == HR_BULK_REBIAS) ||
             (heuristics == HR_BULK_REVOKE)) { //如果是多次撤銷或者多次偏向       
    //批量撤銷
    bulk_revoke_or_rebias_at_safepoint(obj, (heuristics == HR_BULK_REBIAS), false, NULL);
  }
  clean_up_cached_monitor_info();
}

?偏向鎖的釋放,需要等待全局安全點(在這個時間點上沒有正在執(zhí)行的字節(jié)碼)姓言,首先暫停擁有偏向鎖的線程瞬项,然后檢查持有偏向鎖的線程是否還活著,如果線程不處于活動狀態(tài)何荚,則將對象頭設(shè)置成無鎖狀態(tài)囱淋。如果線程仍然活著,則會升級為輕量級鎖餐塘,遍歷偏向?qū)ο蟮乃涗浲滓隆械逆i記錄和對象頭的Mark Word要么重新偏向其他線程,要么恢復(fù)到無鎖戒傻,或者標記對象不適合作為偏向鎖称鳞。最后喚醒暫停的線程。

JVM內(nèi)部為每個類維護了一個偏向鎖revoke計數(shù)器稠鼻,對偏向鎖撤銷進行計數(shù),當這個值達到指定閾值時狂票,JVM會認為這個類的偏向鎖有問題候齿,需要重新偏向(rebias),對所有屬于這個類的對象進行重偏向的操作成為 批量重偏向(bulk rebias)。在做bulk rebias時闺属,會對這個類的epoch的值做遞增慌盯,這個epoch會存儲在對象頭中的epoch字段。在判斷這個對象是否獲得偏向鎖的條件是:markwordbiased_lock:1掂器、lock:01亚皂、threadid和當前線程id相等、epoch字段和所屬類的epoch值相同国瓮,如果epoch的值不一樣灭必,要么就是撤銷偏向鎖、要么就是rebias乃摹; 如果這個類的revoke計數(shù)器的值繼續(xù)增加到一個閾值禁漓,那么jvm會認為這個類不適合偏向鎖,就需要進行bulk revoke操作

輕量級鎖的獲取

?輕量級鎖的獲取孵睬,是調(diào)用 ::slow_enter方法播歼,該方法同樣位于 synchronizer.cpp文件中

// -----------------------------------------------------------------------------
// Interpreter/Compiler Slow Case
// This routine is used to handle interpreter/compiler slow case
// We don't need to use fast path here, because it must have been
// failed in the interpreter/compiler code.
void ObjectSynchronizer::slow_enter(Handle obj, BasicLock* lock, TRAPS) {
  markOop mark = obj->mark();
  assert(!mark->has_bias_pattern(), "should not see bias pattern here");

  if (mark->is_neutral()) { //如果當前是無鎖狀態(tài), markword的biase_lock:0,lock:01
    // Anticipate successful CAS -- the ST of the displaced mark must
    // be visible <= the ST performed by the CAS.
    //直接把mark保存到BasicLock對象的_displaced_header字段
    lock->set_displaced_header(mark);
    //通過CAS將mark word更新為指向BasicLock對象的指針掰读,更新成功表示獲得了輕量級鎖
    if (mark == obj()->cas_set_mark((markOop) lock, mark)) {
      TEVENT(slow_enter: release stacklock);
      return;
    }
    //如果markword處于加鎖狀態(tài)秘狞、且markword中的ptr指針指向當前線程的棧幀叭莫,表示為重入操作,不需要爭搶鎖
    // Fall through to inflate() ...
  } else if (mark->has_locker() &&
             THREAD->is_lock_owned((address)mark->locker())) {
    assert(lock != mark->locker(), "must not re-lock the same lock");
    assert(lock != (BasicLock*)obj->mark(), "don't relock with same BasicLock");
    lock->set_displaced_header(NULL);
    return;
  }

  // The object header will never be displaced to this lock,
  // so it does not matter what the value is, except that it
  // must be non-zero to avoid looking like a re-entrant lock,
  // and must not look locked either.
  //代碼執(zhí)行到這里烁试,說明有多個線程競爭輕量級鎖雇初,輕量級鎖通過`inflate`進行膨脹升級為重量級鎖
  lock->set_displaced_header(markOopDesc::unused_mark());
  ObjectSynchronizer::inflate(THREAD,
                              obj(),
                              inflate_cause_monitor_enter)->enter(THREAD);
}

?輕量級鎖的獲取邏輯如下:

  1. mark->is_neutral()方法, is_neutral這個方法是在 markOop.hpp中定義,如果 biased_lock:0lock:01表示無鎖狀態(tài)

  2. 如果mark處于無鎖狀態(tài)廓潜,則進入步驟(3)抵皱,否則執(zhí)行步驟(5)

  3. mark保存到BasicLock對象的displacedheader字段

  4. 通過CAS嘗試將markword更新為指向BasicLock對象的指針,如果更新成功辩蛋,表示競爭到鎖呻畸,則執(zhí)行同步代碼,否則執(zhí)行步驟(5)

  5. 如果當前mark處于加鎖狀態(tài)悼院,且mark中的ptr指針指向當前線程的棧幀伤为,則執(zhí)行同步代碼,否則說明有多個線程競爭輕量級鎖据途,輕量級鎖需要膨脹升級為重量級鎖

輕量級鎖的釋放

?輕量級鎖的釋放是通過interpreterRuntime.cpp文件中的monitorexit調(diào)用:

//%note monitor_1
IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorexit(JavaThread* thread, BasicObjectLock* elem))
#ifdef ASSERT
  thread->last_frame().interpreter_frame_verify_monitor(elem);
#endif
  Handle h_obj(thread, elem->obj());
  assert(Universe::heap()->is_in_reserved_or_null(h_obj()),
         "must be NULL or an object");
  if (elem == NULL || h_obj()->is_unlocked()) {
    THROW(vmSymbols::java_lang_IllegalMonitorStateException());
  }
  ObjectSynchronizer::slow_exit(h_obj(), elem->lock(), thread);
  // Free entry. This must be done here, since a pending exception might be installed on
  // exit. If it is not cleared, the exception handling code will try to unlock the monitor again.
  elem->set_obj(NULL);
#ifdef ASSERT
  thread->last_frame().interpreter_frame_verify_monitor(elem);
#endif
IRT_END

?這段代碼中主要是通過synchronizer.cpp文件中 ObjectSynchronizer::slow_exit來執(zhí)行

// This routine is used to handle interpreter/compiler slow case
// We don't need to use fast path here, because it must have
// failed in the interpreter/compiler code. Simply use the heavy
// weight monitor should be ok, unless someone find otherwise.
void ObjectSynchronizer::slow_exit(oop object, BasicLock* lock, TRAPS) {
  fast_exit(object, lock, THREAD);
}

? ObjectSynchronizer::fast_exit的代碼如下:

void ObjectSynchronizer::fast_exit(oop object, BasicLock* lock, TRAPS) {
  markOop mark = object->mark(); //獲取線程棧幀中鎖記錄(LockRecord)中的markword
  // We cannot check for Biased Locking if we are racing an inflation.
  assert(mark == markOopDesc::INFLATING() ||
         !mark->has_bias_pattern(), "should not see bias pattern here");

  markOop dhw = lock->displaced_header();
  if (dhw == NULL) {
    // If the displaced header is NULL, then this exit matches up with
    // a recursive enter. No real work to do here except for diagnostics.
#ifndef PRODUCT
    if (mark != markOopDesc::INFLATING()) {
      // Only do diagnostics if we are not racing an inflation. Simply
      // exiting a recursive enter of a Java Monitor that is being
      // inflated is safe; see the has_monitor() comment below.
      assert(!mark->is_neutral(), "invariant");
      assert(!mark->has_locker() ||
             THREAD->is_lock_owned((address)mark->locker()), "invariant");
      if (mark->has_monitor()) {
        // The BasicLock's displaced_header is marked as a recursive
        // enter and we have an inflated Java Monitor (ObjectMonitor).
        // This is a special case where the Java Monitor was inflated
        // after this thread entered the stack-lock recursively. When a
        // Java Monitor is inflated, we cannot safely walk the Java
        // Monitor owner's stack and update the BasicLocks because a
        // Java Monitor can be asynchronously inflated by a thread that
        // does not own the Java Monitor.
        ObjectMonitor * m = mark->monitor();
        assert(((oop)(m->object()))->mark() == mark, "invariant");
        assert(m->is_entered(THREAD), "invariant");
      }
    }
#endif
    return;
  }

  if (mark == (markOop) lock) {
    // If the object is stack-locked by the current thread, try to
    // swing the displaced header from the BasicLock back to the mark.
    assert(dhw->is_neutral(), "invariant");
    //通過CAS嘗試將Displaced Mark Word替換回對象頭绞愚,如果成功,表示鎖釋放成功颖医。
    if (object->cas_set_mark(dhw, mark) == mark) {
      TEVENT(fast_exit: release stack-lock);
      return;
    }
  }

  //鎖膨脹位衩,調(diào)用重量級鎖的釋放鎖方法
  // We have to take the slow-path of possible inflation and then exit.
  ObjectSynchronizer::inflate(THREAD,
                              object,
                              inflate_cause_vm_internal)->exit(true, THREAD);
}

?輕量級鎖的釋放,就是將當前線程棧幀中鎖記錄空間中的Mark Word替換到鎖對象的對象頭中熔萧,如果成功表示鎖釋放成功糖驴。否則,鎖膨脹成重量級鎖佛致,實現(xiàn)重量級鎖的釋放鎖邏輯

鎖膨脹的過程分析

?重量級鎖是通過對象內(nèi)部的監(jiān)視器(monitor)來實現(xiàn)贮缕,而monitor的本質(zhì)是依賴操作系統(tǒng)底層的MutexLock實現(xiàn)的。我們先來看鎖的膨脹過程俺榆,從前面的分析中已經(jīng)知道了所膨脹的過程是通過 ObjectSynchronizer::inflate方法實現(xiàn)的感昼,代碼如下:

ObjectMonitor* ObjectSynchronizer::inflate(Thread * Self,
                                                     oop object,
                                                     const InflateCause cause) {

  // Inflate mutates the heap ...
  // Relaxing assertion for bug 6320749.
  assert(Universe::verify_in_progress() ||
         !SafepointSynchronize::is_at_safepoint(), "invariant");

  EventJavaMonitorInflate event;

  for (;;) { //自旋
    const markOop mark = object->mark();
    assert(!mark->has_bias_pattern(), "invariant");

    // The mark can be in one of the following states:
    // *  Inflated     - just return
    // *  Stack-locked - coerce it to inflated
    // *  INFLATING    - busy wait for conversion to complete
    // *  Neutral      - aggressively inflate the object.
    // *  BIASED       - Illegal.  We should never see this

    // CASE: inflated
    if (mark->has_monitor()) { //has_monitor是markOop.hpp中的方法,如果為true表示當前鎖已經(jīng)是重量級鎖了
      ObjectMonitor * inf = mark->monitor(); //獲得重量級鎖的對象監(jiān)視器直接返回
      assert(inf->header()->is_neutral(), "invariant");
      assert(oopDesc::equals((oop) inf->object(), object), "invariant");
      assert(ObjectSynchronizer::verify_objmon_isinpool(inf), "monitor is invalid");
      return inf;
    }

    // CASE: inflation in progress - inflating over a stack-lock.
    // Some other thread is converting from stack-locked to inflated.
    // Only that thread can complete inflation -- other threads must wait.
    // The INFLATING value is transient.
    // Currently, we spin/yield/park and poll the markword, waiting for inflation to finish.
    // We could always eliminate polling by parking the thread on some auxiliary list.
    if (mark == markOopDesc::INFLATING()) { //膨脹等待罐脊,表示存在線程正在膨脹定嗓,通過continue進行下一輪的膨脹
      TEVENT(Inflate: spin while INFLATING);
      ReadStableMark(object);
      continue;
    }

    // CASE: stack-locked
    // Could be stack-locked either by this thread or by some other thread.
    if (mark->has_locker()) { //表示當前鎖為輕量級鎖,以下是輕量級鎖的膨脹邏輯
      ObjectMonitor * m = omAlloc(Self); //獲取一個可用的ObjectMonitor
      // Optimistically prepare the objectmonitor - anticipate successful CAS
      // We do this before the CAS in order to minimize the length of time
      // in which INFLATING appears in the mark.
      m->Recycle();
      m->_Responsible  = NULL;
      m->_recursions   = 0;
      m->_SpinDuration = ObjectMonitor::Knob_SpinLimit;   // Consider: maintain by type/class
      /**將object->mark_addr()和mark比較萍桌,如果這兩個值相等蜕乡,則將object->mark_addr()
      改成markOopDesc::INFLATING(),相等返回是mark梗夸,不相等返回的是object->mark_addr()**/
      markOop cmp = object->cas_set_mark(markOopDesc::INFLATING(), mark);
      if (cmp != mark) { // CAS失敗
        omRelease(Self, m, true); //釋放監(jiān)視器
        continue;       // Interference -- just retry 
      }

      // fetch the displaced mark from the owner's stack.
      // The owner can't die or unwind past the lock while our INFLATING
      // object is in the mark.  Furthermore the owner can't complete
      // an unlock on the object, either.
      markOop dmw = mark->displaced_mark_helper();
      assert(dmw->is_neutral(), "invariant");

      //CAS成功以后层玲,設(shè)置ObjectMonitor相關(guān)屬性
      // Setup monitor fields to proper values -- prepare the monitor
      m->set_header(dmw);

      // Optimization: if the mark->locker stack address is associated
      // with this thread we could simply set m->_owner = Self.
      // Note that a thread can inflate an object
      // that it has stack-locked -- as might happen in wait() -- directly
      // with CAS.  That is, we can avoid the xchg-NULL .... ST idiom.
      m->set_owner(mark->locker());
      m->set_object(object);
      // TODO-FIXME: assert BasicLock->dhw != 0.

      // Must preserve store ordering. The monitor state must
      // be stable at the time of publishing the monitor address.
      guarantee(object->mark() == markOopDesc::INFLATING(), "invariant");
      object->release_set_mark(markOopDesc::encode(m));

      // Hopefully the performance counters are allocated on distinct cache lines
      // to avoid false sharing on MP systems ...
      OM_PERFDATA_OP(Inflations, inc());
      TEVENT(Inflate: overwrite stacklock);
      if (log_is_enabled(Debug, monitorinflation)) {
        if (object->is_instance()) {
          ResourceMark rm;
          log_debug(monitorinflation)("Inflating object " INTPTR_FORMAT " , mark " INTPTR_FORMAT " , type %s",
                                      p2i(object), p2i(object->mark()),
                                      object->klass()->external_name());
        }
      }
      if (event.should_commit()) {
        post_monitor_inflate_event(&event, object, cause);
      }
      return m; //返回ObjectMonitor
    }

    //如果是無鎖狀態(tài)
    assert(mark->is_neutral(), "invariant");
    ObjectMonitor * m = omAlloc(Self);//獲取一個可用的ObjectMonitor
    // prepare m for installation - set monitor to initial state
    //設(shè)置ObjectMonitor相關(guān)屬性
    m->Recycle();
    m->set_header(mark);
    m->set_owner(NULL);
    m->set_object(object);
    m->_recursions   = 0;
    m->_Responsible  = NULL;
    m->_SpinDuration = ObjectMonitor::Knob_SpinLimit;       // consider: keep metastats by type/class

    /**將object->mark_addr()和mark比較,如果這兩個值相等,則將object->mark_addr()
    改成markOopDesc::encode(m)辛块,相等返回是mark畔派,不相等返回的是object->mark_addr()**/
    if (object->cas_set_mark(markOopDesc::encode(m), mark) != mark) {
      //CAS失敗,說明出現(xiàn)了鎖競爭润绵,則釋放監(jiān)視器重行競爭鎖
      m->set_object(NULL);
      m->set_owner(NULL);
      m->Recycle();
      omRelease(Self, m, true);
      m = NULL;
      continue;
      // interference - the markword changed - just retry.
      // The state-transitions are one-way, so there's no chance of
      // live-lock -- "Inflated" is an absorbing state.
    }

    // Hopefully the performance counters are allocated on distinct
    // cache lines to avoid false sharing on MP systems ...
    OM_PERFDATA_OP(Inflations, inc());
    TEVENT(Inflate: overwrite neutral);
    if (log_is_enabled(Debug, monitorinflation)) {
      if (object->is_instance()) {
        ResourceMark rm;
        log_debug(monitorinflation)("Inflating object " INTPTR_FORMAT " , mark " INTPTR_FORMAT " , type %s",
                                    p2i(object), p2i(object->mark()),
                                    object->klass()->external_name());
      }
    }
    if (event.should_commit()) {
      post_monitor_inflate_event(&event, object, cause);
    }
    return m; //返回ObjectMonitor對象
  }
}

?整個鎖膨脹的過程是通過自旋來完成的线椰,具體的實現(xiàn)邏輯簡答總結(jié)以下幾點:

    1. mark->has_monitor()判斷如果當前鎖對象為重量級鎖,也就是lock:10尘盼,則執(zhí)行(2),否則執(zhí)行(3)憨愉;
    1. 通過mark->monitor獲得重量級鎖的對象監(jiān)視器ObjectMonitor并返回,鎖膨脹過程結(jié)束卿捎;
    1. 如果當前鎖處于INFLATING,說明有其他線程在執(zhí)行鎖膨脹配紫,那么當前線程通過自旋等待其他線程鎖膨脹完成;
    1. 如果當前是輕量級鎖狀態(tài)mark->has_locker(),則進行鎖膨脹午阵。首先躺孝,通過omAlloc方法獲得一個可用的;ObjectMonitor底桂,并設(shè)置初始數(shù)據(jù)植袍;然后通過CAS將對象頭設(shè)置為markOopDesc:INFLATING,表示當前鎖正在膨脹籽懦,如果CAS失敗于个,繼續(xù)自旋;
    1. 如果是無鎖狀態(tài)暮顺,邏輯類似第4步驟

鎖膨脹的過程實際上是獲得一個ObjectMonitor對象監(jiān)視器览濒,而真正搶占鎖的邏輯,在ObjectMonitor::enter方法里面拖云;

重量級鎖的競爭

?重量級鎖的競爭,在 ObjectMonitor::enter方法中应又,代碼文件在 objectMonitor.cpp重量級鎖的代碼就不一一分析了宙项,簡單說一下下面這段代碼主要做的幾件事:

  • 通過CASmonitor_owner字段設(shè)置為當前線程,如果設(shè)置成功株扛,則直接返回尤筐;
  • 如果之前的_owner指向的是當前的線程,說明是重入洞就,執(zhí)行_recursions++增加重入次數(shù)盆繁;
  • 如果當前線程獲取監(jiān)視器鎖成功,將 _recursions設(shè)置為1旬蟋, _owner設(shè)置為當前線程油昂;
  • 如果獲取鎖失敗,則等待鎖釋放;
void ObjectMonitor::enter(TRAPS) {
  // The following code is ordered to check the most common cases first
  // and to reduce RTS->RTO cache line upgrades on SPARC and IA32 processors.
  Thread * const Self = THREAD;

  void * cur = Atomic::cmpxchg(Self, &_owner, (void*)NULL);
  if (cur == NULL) { //CAS成功
    // Either ASSERT _recursions == 0 or explicitly set _recursions = 0.
    assert(_recursions == 0, "invariant");
    assert(_owner == Self, "invariant");
    return;
  }

  if (cur == Self) {
    // TODO-FIXME: check for integer overflow!  BUGID 6557169.
    _recursions++;
    return;
  }

  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;
    return;
  }

  // We've encountered genuine contention.
  assert(Self->_Stalled == 0, "invariant");
  Self->_Stalled = intptr_t(this);

  // Try one round of spinning *before* enqueueing Self
  // and before going through the awkward and expensive state
  // transitions.  The following spin is strictly optional ...
  // Note that if we acquire the monitor from an initial spin
  // we forgo posting JVMTI events and firing DTRACE probes.
  if (Knob_SpinEarly && TrySpin (Self) > 0) {
    assert(_owner == Self, "invariant");
    assert(_recursions == 0, "invariant");
    assert(((oop)(object()))->mark() == markOopDesc::encode(this), "invariant");
    Self->_Stalled = 0;
    return;
  }

  assert(_owner != Self, "invariant");
  assert(_succ != Self, "invariant");
  assert(Self->is_Java_thread(), "invariant");
  JavaThread * jt = (JavaThread *) Self;
  assert(!SafepointSynchronize::is_at_safepoint(), "invariant");
  assert(jt->thread_state() != _thread_blocked, "invariant");
  assert(this->object() != NULL, "invariant");
  assert(_count >= 0, "invariant");

  // Prevent deflation at STW-time.  See deflate_idle_monitors() and is_busy().
  // Ensure the object-monitor relationship remains stable while there's contention.
  Atomic::inc(&_count);

  JFR_ONLY(JfrConditionalFlushWithStacktrace<EventJavaMonitorEnter> flush(jt);)
  EventJavaMonitorEnter event;
  if (event.should_commit()) {
    event.set_monitorClass(((oop)this->object())->klass());
    event.set_address((uintptr_t)(this->object_addr()));
  }

  { // Change java thread status to indicate blocked on monitor enter.
    JavaThreadBlockedOnMonitorEnterState jtbmes(jt, this);

    Self->set_current_pending_monitor(this);

    DTRACE_MONITOR_PROBE(contended__enter, this, object(), jt);
    if (JvmtiExport::should_post_monitor_contended_enter()) {
      JvmtiExport::post_monitor_contended_enter(jt, this);

      // The current thread does not yet own the monitor and does not
      // yet appear on any queues that would get it made the successor.
      // This means that the JVMTI_EVENT_MONITOR_CONTENDED_ENTER event
      // handler cannot accidentally consume an unpark() meant for the
      // ParkEvent associated with this ObjectMonitor.
    }

    OSThreadContendState osts(Self->osthread());
    ThreadBlockInVM tbivm(jt);

    // TODO-FIXME: change the following for(;;) loop to straight-line code.
    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(false, Self);

      jt->java_suspend_self();
    }
    Self->set_current_pending_monitor(NULL);
  }

  Atomic::dec(&_count);
  assert(_count >= 0, "invariant");
  Self->_Stalled = 0;

  // Must either set _recursions = 0 or ASSERT _recursions == 0.
  assert(_recursions == 0, "invariant");
  assert(_owner == Self, "invariant");
  assert(_succ != Self, "invariant");
  assert(((oop)(object()))->mark() == markOopDesc::encode(this), "invariant");

  DTRACE_MONITOR_PROBE(contended__entered, this, object(), jt);
  if (JvmtiExport::should_post_monitor_contended_entered()) {
    JvmtiExport::post_monitor_contended_entered(jt, this);

    // The current thread already owns the monitor and is not going to
    // call park() for the remainder of the monitor enter protocol. So
    // it doesn't matter if the JVMTI_EVENT_MONITOR_CONTENDED_ENTERED
    // event handler consumed an unpark() issued by the thread that
    // just exited the monitor.
  }
  if (event.should_commit()) {
    event.set_previousOwner((uintptr_t)_previous_owner_tid);
    event.commit();
  }
  OM_PERFDATA_OP(ContendedLockAttempts, inc());
}

?如果獲取鎖失敗冕碟,則需要通過自旋的方式等待鎖釋放拦惋,自旋執(zhí)行的方法是 ObjectMonitor::EnterI,部分代碼如下

  • 將當前線程封裝成ObjectWaiter對象node安寺,狀態(tài)設(shè)置成TS_CXQ厕妖;
  • 通過自旋操作將node節(jié)點push到_cxq隊列;
  • node節(jié)點添加到_cxq隊列之后挑庶,繼續(xù)通過自旋嘗試獲取鎖言秸,如果在指定的閾值范圍內(nèi)沒有獲得鎖,則通過park將當前線程掛起迎捺,等待被喚醒举畸;
void ObjectMonitor::EnterI(TRAPS) {
  Thread * const Self = THREAD;
  assert(Self->is_Java_thread(), "invariant");
  assert(((JavaThread *) Self)->thread_state() == _thread_blocked, "invariant");

  // Try the lock - TATAS
  if (TryLock (Self) > 0) {
    assert(_succ != Self, "invariant");
    assert(_owner == Self, "invariant");
    assert(_Responsible != Self, "invariant");
    return;
  }

  DeferredInitialize();

  // We try one round of spinning *before* enqueueing Self.
  //
  // If the _owner is ready but OFFPROC we could use a YieldTo()
  // operation to donate the remainder of this thread's quantum
  // to the owner.  This has subtle but beneficial affinity
  // effects.

  if (TrySpin (Self) > 0) {
    assert(_owner == Self, "invariant");
    assert(_succ != Self, "invariant");
    assert(_Responsible != Self, "invariant");
    return;
  }

  // The Spin failed -- Enqueue and park the thread ...
  assert(_succ != Self, "invariant");
  assert(_owner != Self, "invariant");
  assert(_Responsible != Self, "invariant");

  // Enqueue "Self" on ObjectMonitor's _cxq.
  //
  // Node acts as a proxy for Self.
  // As an aside, if were to ever rewrite the synchronization code mostly
  // in Java, WaitNodes, ObjectMonitors, and Events would become 1st-class
  // Java objects.  This would avoid awkward lifecycle and liveness issues,
  // as well as eliminate a subset of ABA issues.
  // TODO: eliminate ObjectWaiter and enqueue either Threads or Events.

  ObjectWaiter node(Self);
  Self->_ParkEvent->reset();
  node._prev   = (ObjectWaiter *) 0xBAD;
  node.TState  = ObjectWaiter::TS_CXQ;

  // Push "Self" onto the front of the _cxq.
  // Once on cxq/EntryList, Self stays on-queue until it acquires the lock.
  // Note that spinning tends to reduce the rate at which threads
  // enqueue and dequeue on EntryList|cxq.
  ObjectWaiter * nxt;
  for (;;) {
    node._next = nxt = _cxq;
    if (Atomic::cmpxchg(&node, &_cxq, nxt) == nxt) break;

    // Interference - the CAS failed because _cxq changed.  Just retry.
    // As an optional optimization we retry the lock.
    if (TryLock (Self) > 0) {
      assert(_succ != Self, "invariant");
      assert(_owner == Self, "invariant");
      assert(_Responsible != Self, "invariant");
      return;
    }
  }


  if ((SyncFlags & 16) == 0 && nxt == NULL && _EntryList == NULL) {
    // Try to assume the role of responsible thread for the monitor.
    // CONSIDER:  ST vs CAS vs { if (Responsible==null) Responsible=Self }
    Atomic::replace_if_null(Self, &_Responsible);
  }

  // The lock might have been released while this thread was occupied queueing
  // itself onto _cxq.  To close the race and avoid "stranding" and
  // progress-liveness failure we must resample-retry _owner before parking.
  // Note the Dekker/Lamport duality: ST cxq; MEMBAR; LD Owner.
  // In this case the ST-MEMBAR is accomplished with CAS().
  //
  // TODO: Defer all thread state transitions until park-time.
  // Since state transitions are heavy and inefficient we'd like
  // to defer the state transitions until absolutely necessary,
  // and in doing so avoid some transitions ...

  TEVENT(Inflated enter - Contention);
  int nWakeups = 0;
  int recheckInterval = 1;

  //node節(jié)點添加到_cxq隊列之后,繼續(xù)通過自旋嘗試獲取鎖破加,如果在指定的閾值范圍內(nèi)沒有獲得鎖俱恶,則通過park將當前線程掛起,等待被喚醒
  for (;;) {

    if (TryLock(Self) > 0) break;
    assert(_owner != Self, "invariant");

    if ((SyncFlags & 2) && _Responsible == NULL) {
      Atomic::replace_if_null(Self, &_Responsible);
    }

    // park self
    if (_Responsible == Self || (SyncFlags & 1)) {
      TEVENT(Inflated enter - park TIMED);
      Self->_ParkEvent->park((jlong) recheckInterval);
      // Increase the recheckInterval, but clamp the value.
      recheckInterval *= 8;
      if (recheckInterval > MAX_RECHECK_INTERVAL) {
        recheckInterval = MAX_RECHECK_INTERVAL;
      }
    } else {
      TEVENT(Inflated enter - park UNTIMED);
      Self->_ParkEvent->park();
    }

    if (TryLock(Self) > 0) break;

    // The lock is still contested.
    // Keep a tally of the # of futile wakeups.
    // Note that the counter is not protected by a lock or updated by atomics.
    // That is by design - we trade "lossy" counters which are exposed to
    // races during updates for a lower probe effect.
    TEVENT(Inflated enter - Futile wakeup);
    // This PerfData object can be used in parallel with a safepoint.
    // See the work around in PerfDataManager::destroy().
    OM_PERFDATA_OP(FutileWakeups, inc());
    ++nWakeups;

    // Assuming this is not a spurious wakeup we'll normally find _succ == Self.
    // We can defer clearing _succ until after the spin completes
    // TrySpin() must tolerate being called with _succ == Self.
    // Try yet another round of adaptive spinning.
    if ((Knob_SpinAfterFutile & 1) && TrySpin(Self) > 0) break;

    // We can find that we were unpark()ed and redesignated _succ while
    // we were spinning.  That's harmless.  If we iterate and call park(),
    // park() will consume the event and return immediately and we'll
    // just spin again.  This pattern can repeat, leaving _succ to simply
    // spin on a CPU.  Enable Knob_ResetEvent to clear pending unparks().
    // Alternately, we can sample fired() here, and if set, forgo spinning
    // in the next iteration.

    if ((Knob_ResetEvent & 1) && Self->_ParkEvent->fired()) {
      Self->_ParkEvent->reset();
      OrderAccess::fence();
    }
    if (_succ == Self) _succ = NULL;

    // Invariant: after clearing _succ a thread *must* retry _owner before parking.
    OrderAccess::fence();
  }

  // Egress :
  // Self has acquired the lock -- Unlink Self from the cxq or EntryList.
  // Normally we'll find Self on the EntryList .
  // From the perspective of the lock owner (this thread), the
  // EntryList is stable and cxq is prepend-only.
  // The head of cxq is volatile but the interior is stable.
  // In addition, Self.TState is stable.

  assert(_owner == Self, "invariant");
  assert(object() != NULL, "invariant");
  // I'd like to write:
  //   guarantee (((oop)(object()))->mark() == markOopDesc::encode(this), "invariant") ;
  // but as we're at a safepoint that's not safe.

  UnlinkAfterAcquire(Self, &node);
  if (_succ == Self) _succ = NULL;

  assert(_succ != Self, "invariant");
  if (_Responsible == Self) {
    _Responsible = NULL;
    OrderAccess::fence(); // Dekker pivot-point
  }


  if (SyncFlags & 8) {
    OrderAccess::fence();
  }
  return;
}

?TryLock(self)的代碼是在 ObjectMonitor::TryLock定義的范舀,代碼的實現(xiàn)如下:

代碼的實現(xiàn)原理很簡單合是,通過自旋,CAS設(shè)置monitor_owner字段為當前線程锭环,如果成功聪全,表示獲取到了鎖,如果失敗辅辩,則繼續(xù)被掛起难礼。

// Caveat: TryLock() is not necessarily serializing if it returns failure.
// Callers must compensate as needed.

int ObjectMonitor::TryLock(Thread * Self) {
  void * own = _owner;
  if (own != NULL) return 0;
  if (Atomic::replace_if_null(Self, &_owner)) {
    // Either guarantee _recursions == 0 or set _recursions = 0.
    assert(_recursions == 0, "invariant");
    assert(_owner == Self, "invariant");
    return 1;
  }
  // The lock had been free momentarily, but we lost the race to the lock.
  // Interference -- the CAS failed.
  // We can either return -1 or retry.
  // Retry doesn't make as much sense because the lock was just acquired.
  return -1;
}

重量級鎖的釋放

?重量級鎖的釋放是通過 ObjectMonitor::exit來實現(xiàn)的,釋放以后會通知被阻塞的線程去競爭鎖:

  • 判斷當前鎖對象中的owner沒有指向當前線程玫锋,如果owner指向的BasicLock在當前線程棧上,那么將_owner指向當前線程蛾茉;
  • 如果當前鎖對象中的_owner指向當前線程,則判斷當前線程重入鎖的次數(shù)撩鹿,如果不為0谦炬,繼續(xù)執(zhí)行ObjectMonitor::exit(),直到重入鎖次數(shù)為0為止节沦;
  • 釋放當前鎖键思,并根據(jù)QMode的模式判斷,是將_cxq中掛起的線程喚醒甫贯,還是其他操作吼鳞;
void ObjectMonitor::exit(bool not_suspended, TRAPS) {
  Thread * const Self = THREAD;
  if (THREAD != _owner) { //如果當前鎖對象中的_owner沒有指向當前線程
    //如果_owner指向的BasicLock在當前線程棧上,那么將_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;
    } else {
      // Apparent unbalanced locking ...
      // Naively we'd like to throw IllegalMonitorStateException.
      // As a practical matter we can neither allocate nor throw an
      // exception as ::exit() can be called from leaf routines.
      // see x86_32.ad Fast_Unlock() and the I1 and I2 properties.
      // Upon deeper reflection, however, in a properly run JVM the only
      // way we should encounter this situation is in the presence of
      // unbalanced JNI locking. TODO: CheckJNICalls.
      // See also: CR4414101
      TEVENT(Exit - Throw IMSX);
      assert(false, "Non-balanced monitor enter/exit! Likely JNI locking");
      return;
    }
  }


  //如果當前,線程重入鎖的次數(shù)叫搁,不為0赔桌,那么就重新走ObjectMonitor::exit供炎,直到重入鎖次數(shù)為0為止
  if (_recursions != 0) {
    _recursions--;        // this is simple recursive enter
    TEVENT(Inflated exit - recursive);
    return;
  }

  // Invariant: after setting Responsible=null an thread must execute
  // a MEMBAR or other serializing instruction before fetching EntryList|cxq.
  if ((SyncFlags & 4) == 0) {
    _Responsible = NULL;
  }

#if INCLUDE_JFR
  // get the owner's thread id for the MonitorEnter event
  // if it is enabled and the thread isn't suspended
  if (not_suspended && EventJavaMonitorEnter::is_enabled()) {
    _previous_owner_tid = JFR_THREAD_ID(Self);
  }
#endif

  for (;;) {
    assert(THREAD == _owner, "invariant");

    if (Knob_ExitPolicy == 0) {    
      OrderAccess::release_store(&_owner, (void*)NULL);   // drop the lock
      OrderAccess::storeload();                        // See if we need to wake a successor
      if ((intptr_t(_EntryList)|intptr_t(_cxq)) == 0 || _succ != NULL) {
        TEVENT(Inflated exit - simple egress);
        return;
      }
      TEVENT(Inflated exit - complex egress);
      
      if (!Atomic::replace_if_null(THREAD, &_owner)) {
        return;
      }
      TEVENT(Exit - Reacquired);
    } else {
      if ((intptr_t(_EntryList)|intptr_t(_cxq)) == 0 || _succ != NULL) {
        OrderAccess::release_store(&_owner, (void*)NULL);   // drop the lock
        OrderAccess::storeload();
        // Ratify the previously observed values.
        if (_cxq == NULL || _succ != NULL) {
          TEVENT(Inflated exit - simple egress);
          return;
        }

        if (!Atomic::replace_if_null(THREAD, &_owner)) {
          TEVENT(Inflated exit - reacquired succeeded);
          return;
        }
        TEVENT(Inflated exit - reacquired failed);
      } else {
        TEVENT(Inflated exit - complex egress);
      }
    }

    guarantee(_owner == THREAD, "invariant");

    ObjectWaiter * w = NULL;
    int QMode = Knob_QMode;

    //根據(jù)QMode的模式判斷,
    //如果QMode == 2則直接從_cxq掛起的線程中喚醒
    if (QMode == 2 && _cxq != NULL) {
      // QMode == 2 : cxq has precedence over EntryList.
      // Try to directly wake a successor from the cxq.
      // If successful, the successor will need to unlink itself from cxq.
      w = _cxq;
      assert(w != NULL, "invariant");
      assert(w->TState == ObjectWaiter::TS_CXQ, "Invariant");
      ExitEpilog(Self, w);
      return;
    }

    if (QMode == 3 && _cxq != NULL) {
      // Aggressively drain cxq into EntryList at the first opportunity.
      // This policy ensure that recently-run threads live at the head of EntryList.
      // Drain _cxq into EntryList - bulk transfer.
      // First, detach _cxq.
      // The following loop is tantamount to: w = swap(&cxq, NULL)
      w = _cxq;
      for (;;) {
        assert(w != NULL, "Invariant");
        ObjectWaiter * u = Atomic::cmpxchg((ObjectWaiter*)NULL, &_cxq, w);
        if (u == w) break;
        w = u;
      }
      assert(w != NULL, "invariant");

      ObjectWaiter * q = NULL;
      ObjectWaiter * p;
      for (p = w; p != NULL; p = p->_next) {
        guarantee(p->TState == ObjectWaiter::TS_CXQ, "Invariant");
        p->TState = ObjectWaiter::TS_ENTER;
        p->_prev = q;
        q = p;
      }

      // Append the RATs to the EntryList
      // TODO: organize EntryList as a CDLL so we can locate the tail in constant-time.
      ObjectWaiter * Tail;
      for (Tail = _EntryList; Tail != NULL && Tail->_next != NULL;
           Tail = Tail->_next)
        /* empty */;
      if (Tail == NULL) {
        _EntryList = w;
      } else {
        Tail->_next = w;
        w->_prev = Tail;
      }

      // Fall thru into code that tries to wake a successor from EntryList
    }

    if (QMode == 4 && _cxq != NULL) {
      // Aggressively drain cxq into EntryList at the first opportunity.
      // This policy ensure that recently-run threads live at the head of EntryList.

      // Drain _cxq into EntryList - bulk transfer.
      // First, detach _cxq.
      // The following loop is tantamount to: w = swap(&cxq, NULL)
      w = _cxq;
      for (;;) {
        assert(w != NULL, "Invariant");
        ObjectWaiter * u = Atomic::cmpxchg((ObjectWaiter*)NULL, &_cxq, w);
        if (u == w) break;
        w = u;
      }
      assert(w != NULL, "invariant");

      ObjectWaiter * q = NULL;
      ObjectWaiter * p;
      for (p = w; p != NULL; p = p->_next) {
        guarantee(p->TState == ObjectWaiter::TS_CXQ, "Invariant");
        p->TState = ObjectWaiter::TS_ENTER;
        p->_prev = q;
        q = p;
      }

      // Prepend the RATs to the EntryList
      if (_EntryList != NULL) {
        q->_next = _EntryList;
        _EntryList->_prev = q;
      }
      _EntryList = w;

      // Fall thru into code that tries to wake a successor from EntryList
    }

    w = _EntryList;
    if (w != NULL) {    
      assert(w->TState == ObjectWaiter::TS_ENTER, "invariant");
      ExitEpilog(Self, w);
      return;
    }

    // If we find that both _cxq and EntryList are null then just
    // re-run the exit protocol from the top.
    w = _cxq;
    if (w == NULL) continue;

    // Drain _cxq into EntryList - bulk transfer.
    // First, detach _cxq.
    // The following loop is tantamount to: w = swap(&cxq, NULL)
    for (;;) {
      assert(w != NULL, "Invariant");
      ObjectWaiter * u = Atomic::cmpxchg((ObjectWaiter*)NULL, &_cxq, w);
      if (u == w) break;
      w = u;
    }
    TEVENT(Inflated exit - drain cxq into EntryList);

    assert(w != NULL, "invariant");
    assert(_EntryList == NULL, "invariant");

    // Convert the LIFO SLL anchored by _cxq into a DLL.
    // The list reorganization step operates in O(LENGTH(w)) time.
    // It's critical that this step operate quickly as
    // "Self" still holds the outer-lock, restricting parallelism
    // and effectively lengthening the critical section.
    // Invariant: s chases t chases u.
    // TODO-FIXME: consider changing EntryList from a DLL to a CDLL so
    // we have faster access to the tail.

    if (QMode == 1) {
      // QMode == 1 : drain cxq to EntryList, reversing order
      // We also reverse the order of the list.
      ObjectWaiter * s = NULL;
      ObjectWaiter * t = w;
      ObjectWaiter * u = NULL;
      while (t != NULL) {
        guarantee(t->TState == ObjectWaiter::TS_CXQ, "invariant");
        t->TState = ObjectWaiter::TS_ENTER;
        u = t->_next;
        t->_prev = u;
        t->_next = s;
        s = t;
        t = u;
      }
      _EntryList  = s;
      assert(s != NULL, "invariant");
    } else {
      // QMode == 0 or QMode == 2
      _EntryList = w;
      ObjectWaiter * q = NULL;
      ObjectWaiter * p;
      for (p = w; p != NULL; p = p->_next) {
        guarantee(p->TState == ObjectWaiter::TS_CXQ, "Invariant");
        p->TState = ObjectWaiter::TS_ENTER;
        p->_prev = q;
        q = p;
      }
    }

    // In 1-0 mode we need: ST EntryList; MEMBAR #storestore; ST _owner = NULL
    // The MEMBAR is satisfied by the release_store() operation in ExitEpilog().

    // See if we can abdicate to a spinner instead of waking a thread.
    // A primary goal of the implementation is to reduce the
    // context-switch rate.
    if (_succ != NULL) continue;

    w = _EntryList;
    if (w != NULL) {
      guarantee(w->TState == ObjectWaiter::TS_ENTER, "invariant");
      ExitEpilog(Self, w);
      return;
    }
  }
}

?根據(jù)不同的策略(由QMode指定)纬乍,從cxqEntryList中獲取頭節(jié)點碱茁,通過ObjectMonitor::ExitEpilog方法喚醒該節(jié)點封裝的線程,喚醒操作最終由unpark完成:

void ObjectMonitor::ExitEpilog(Thread * Self, ObjectWaiter * Wakee) {
  assert(_owner == Self, "invariant");

  // Exit protocol:
  // 1. ST _succ = wakee
  // 2. membar #loadstore|#storestore;
  // 2. ST _owner = NULL
  // 3. unpark(wakee)

  _succ = Knob_SuccEnabled ? Wakee->_thread : NULL;
  ParkEvent * Trigger = Wakee->_event;

  // Hygiene -- once we've set _owner = NULL we can't safely dereference Wakee again.
  // The thread associated with Wakee may have grabbed the lock and "Wakee" may be
  // out-of-scope (non-extant).
  Wakee  = NULL;

  // Drop the lock
  OrderAccess::release_store(&_owner, (void*)NULL);
  OrderAccess::fence();                               // ST _owner vs LD in unpark()

  if (SafepointMechanism::poll(Self)) {
    TEVENT(unpark before SAFEPOINT);
  }

  DTRACE_MONITOR_PROBE(contended__exit, this, object(), Self);
  Trigger->unpark();  //unpark喚醒線程

  // Maintain stats and report events to JVMTI
  OM_PERFDATA_OP(Parks, inc());
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末仿贬,一起剝皮案震驚了整個濱河市纽竣,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌茧泪,老刑警劉巖蜓氨,帶你破解...
    沈念sama閱讀 212,599評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異队伟,居然都是意外死亡穴吹,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評論 3 385
  • 文/潘曉璐 我一進店門嗜侮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來港令,“玉大人,你說我怎么就攤上這事锈颗≈妫” “怎么了?”我有些...
    開封第一講書人閱讀 158,084評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長阁危。 經(jīng)常有香客問我捆姜,道長,這世上最難降的妖魔是什么永脓? 我笑而不...
    開封第一講書人閱讀 56,708評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮常摧,結(jié)果婚禮上搅吁,老公的妹妹穿的比我還像新娘。我一直安慰自己那婉,他們只是感情好板甘,可當我...
    茶點故事閱讀 65,813評論 6 386
  • 文/花漫 我一把揭開白布盐类。 她就那樣靜靜地躺著寞奸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪在跳。 梳的紋絲不亂的頭發(fā)上枪萄,一...
    開封第一講書人閱讀 50,021評論 1 291
  • 那天,我揣著相機與錄音猫妙,去河邊找鬼瓷翻。 笑死,一個胖子當著我的面吹牛割坠,可吹牛的內(nèi)容都是我干的齐帚。 我是一名探鬼主播,決...
    沈念sama閱讀 39,120評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼彼哼,長吁一口氣:“原來是場噩夢啊……” “哼对妄!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起敢朱,我...
    開封第一講書人閱讀 37,866評論 0 268
  • 序言:老撾萬榮一對情侶失蹤剪菱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后拴签,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體孝常,經(jīng)...
    沈念sama閱讀 44,308評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,633評論 2 327
  • 正文 我和宋清朗相戀三年篓吁,在試婚紗的時候發(fā)現(xiàn)自己被綠了茫因。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,768評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡杖剪,死狀恐怖冻押,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情盛嘿,我是刑警寧澤洛巢,帶...
    沈念sama閱讀 34,461評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站次兆,受9級特大地震影響稿茉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜芥炭,卻給世界環(huán)境...
    茶點故事閱讀 40,094評論 3 317
  • 文/蒙蒙 一漓库、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧园蝠,春花似錦渺蒿、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,850評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽少态。三九已至,卻和暖如春嫌佑,著一層夾襖步出監(jiān)牢的瞬間澳骤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,082評論 1 267
  • 我被黑心中介騙來泰國打工摊册, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留茅特,地道東北人棋枕。 一個月前我還...
    沈念sama閱讀 46,571評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像兵睛,于是被迫代替她去往敵國和親窥浪。 傳聞我的和親對象是個殘疾皇子漾脂,可洞房花燭夜當晚...
    茶點故事閱讀 43,666評論 2 350

推薦閱讀更多精彩內(nèi)容