Synchronized鎖升級底層原理

思考問題

  • 首先請您思考下面的問題:
  1. Synchronized鎖同步機(jī)制性能不好嘛?
  2. 一個(gè)對象天生對應(yīng)一個(gè)monitor鎖嗎褪测?
  3. 為什么說synchronized是非公平鎖滞磺?

synchronized字節(jié)碼

  1. 使用java反編譯,javap -c -p -v class文件
  2. 使用jclasslib插件,更加方便快捷
    public synchronized int getAge(){
        return 18 ;
    }
    //synchronized使用在實(shí)例方法上標(biāo)記為ACC_SYNCHRONIZED,如果是類方法ACC_STATIC
    public synchronized int getAge();
    descriptor: ()I
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=1, locals=1, args_size=1
         0: bipush        18
         2: ireturn
    
    public  String setName(){
        synchronized (this){
            return "shiq";
        }
    }
    
    public java.lang.String setName();
        Code:
           0: aload_0 //一個(gè)局部變量下標(biāo)0加載到操作棧即將this加載到操作數(shù)棧
           1: dup  //復(fù)制
           2: astore_1 //數(shù)值從操作數(shù)棧存儲到局部變量表
           3: monitorenter  //對操作數(shù)棧頂元素加鎖即this
           4: ldc           #2                  // String shiq
           6: aload_1
           7: monitorexit
           8: areturn
           9: astore_2
          10: aload_1
          11: monitorexit
          12: aload_2
          13: athrow
        Exception table: //方法異常表虐沥,保證能夠釋放鎖
           from    to  target type
               4     8     9   any
               9    12     9   any
    
    //如果將上方替換成反編譯后
    synchronized (SynchronizedTest.class)
        0: ldc           #2   // class SynchronizedTest
        2: dup
        3: astore_1
        4: monitorenter 
        
        //0行表示將一個(gè)常量池中位置為2的常量加載到操作數(shù)棧,查詢常量池
       #2 = Class    #24  // SynchronizedTest  
    
  • 以上我們可以看到synchronized修飾this表示對象本身加鎖,修飾class表示對類加鎖

對象內(nèi)存構(gòu)成

  • 一個(gè)對象的內(nèi)存構(gòu)成有對象頭欲险,實(shí)例數(shù)據(jù)镐依,對齊填充
oop層級
  • oopDesc是對象類的頂層基類,每個(gè)Java Object 在 JVM 內(nèi)部都有一個(gè) native 的 C++ 對象 oop/oopDesc 與之對應(yīng)天试。源碼位置 openjdk\hotspot\src\share\vm\oops\oop.hpp
  1. 每個(gè)對象頭由兩部分組成槐壳,klass pointer和Mark Word和Array length(只有是數(shù)組才會有)源碼在vm/oops/oop.hpp中定義的OopDes
    • _mark表示對象標(biāo)記、屬于markOop類型喜每,也就是接下來要講解的Mark World务唐,它記錄了對象和鎖有關(guān)的信息
    • Klass Pointer對象指向它的類元數(shù)據(jù)的指針,虛擬機(jī)通過這個(gè)指針來確定對象是哪個(gè)類的實(shí)例带兜。即下方的_metadata表示類元信息枫笛,類元信息存儲的是對象指向它的類元數(shù)據(jù)(Klass)的首地址,其中Klass表示普通指針刚照、 _compressed_klass表示壓縮類指針
  2. 實(shí)例數(shù)據(jù):按對象頭為基址做相對偏移后操作 obj_field_addr(offset偏移量)
  3. 對齊填充 :java默認(rèn)的8字節(jié)對齊規(guī)則刑巧,如果占位不足要補(bǔ)齊
class oopDesc {
  friend class VMStructs;
 private:
  volatile markOop  _mark; ////markOop:Mark Word標(biāo)記字段
  union _metadata {
    Klass*      _klass; ////對象類型元數(shù)據(jù)的指針
    narrowKlass _compressed_klass;
  } _metadata;
  
 public:
  markOop  mark() const         { return _mark; } //返回Mark Word數(shù)值
  markOop* mark_addr() const    { return (markOop*) &_mark; } //返回一個(gè)地址值為_mark

 public:
  // Need this as public for garbage collection.
  template <class T> T* obj_field_addr(int offset) const;

}

oopDes結(jié)構(gòu)圖:


image
對象頭中Mark World
  • markOopDesc 位于openjdk\hotspot\src\share\vm\oops\markOop.hpp 繼承oopDesc
//mark Word 在markOopDesc中有注釋
//  32 bits:
//  --------
//             hash:25 ------------>| age:4    biased_lock:1 lock:2 (normal object)
//             JavaThread*:23 epoch:2 age:4    biased_lock:1 lock:2 (biased object)
//             size:32 ------------------------------------------>| (CMS free block)
//             PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)
//
//  64 bits:
//  --------
//  unused:25 hash:31 -->| unused:1   age:4    biased_lock:1 lock:2 (normal object)
//  JavaThread*:54 epoch:2 unused:1   age:4    biased_lock:1 lock:2 (biased object)
//  PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object)
//  size:64 ----------------------------------------------------->| (CMS free block)
/**
通過以上可知是個(gè)32bit數(shù)值,不太可能是指針
*/
//觀察其中的方法 无畔,是否是重量級鎖monitor_value = 2 => 鎖標(biāo)記為 10 啊楚, 如果value()為重量級鎖標(biāo)記則***10 & 10  != 0成立
//因此value()函數(shù)代表的即為當(dāng)前obj的mark Word數(shù)值
bool has_monitor() const {
    return ((value() & monitor_value) != 0);
  }
 
  private:
  // Conversion
  uintptr_t value() const { return (uintptr_t) this; }
  
 //是否設(shè)置了偏向標(biāo)志 biased_lock_mask_in_place = 3 
 bool has_bias_pattern() const {
    return (mask_bits(value(), biased_lock_mask_in_place) == biased_lock_pattern);
  }
  • mark word 圖解


    image
  • 以上分析也可以很容易理解了markOopDesc中函數(shù)monitor: 返回一個(gè)ObjectMonitor指針對象 這個(gè)ObjectMonitor 其實(shí)就是對象監(jiān)視器
ObjectMonitor* monitor() const {
    assert(has_monitor(), "check"); //斷定為重量級鎖
    // Use xor instead of &~ to provide one extra tag-bit check.
    return (ObjectMonitor*) (value() ^ monitor_value); //將Mark Word值轉(zhuǎn)化成指針,指向的就是obj的ObjectMonitor鎖對象
  } 
  • 用于返回一個(gè)ObjectMonitor鎖對象浑彰,方法在vm/runtime/synchronizer.cpp中

鎖入口

  • 無論是synchronized代碼塊和synchronized方法恭理,其底層獲取鎖的邏輯都是一樣的。我們以代碼塊的monitorentor和monitorexit為例
  • monitorenter指令在HotSpot的中有兩處地方對monitorenter指令進(jìn)行解析:一個(gè)是在bytecodeInterpreter.cpp 郭变,另一個(gè)是在templateTable_x86_64.cpp
    • 前者是JVM字節(jié)碼解釋器,有C++書寫颜价,優(yōu)點(diǎn)是實(shí)現(xiàn)相對簡單且容易理解,缺點(diǎn)是執(zhí)行慢诉濒;
    • 后者是模板解釋器:對每個(gè)指令都寫了一段對應(yīng)的匯編代碼拍嵌,啟動時(shí)將每個(gè)指令與對應(yīng)匯編代碼入口綁定,效率極高但理解不易循诉;

偏向鎖

  • 首先理解兩個(gè)類BasicObjectLock,BasicLock BasicObjectLock將特定的Java對象與BasicLock關(guān)聯(lián)撇他。
class BasicObjectLock VALUE_OBJ_CLASS_SPEC {
  friend class VMStructs;
 private:
  BasicLock _lock;  //BasicLock對象
  oop       _obj;   //與上方_lock關(guān)聯(lián)的java對象

 public:
  oop   obj() const { return _obj;  }
  void set_obj(oop obj) { _obj = obj; }
  BasicLock* lock()  { return &_lock; }
  
class BasicLock VALUE_OBJ_CLASS_SPEC {
  friend class VMStructs;
 private:
  volatile markOop _displaced_header; //mark word字段
 public:
  markOop      displaced_header() const { return _displaced_header; }
  void  set_displaced_header(markOop header)   { _displaced_header = header; }

  void print_on(outputStream* st) const;

  // move a basic lock (used during deoptimization
  void move_to(oop obj, BasicLock* dest);

  static int displaced_header_offset_in_bytes(){ return offset_of(BasicLock, _displaced_header); }
};
  • BasicLock中的markOop其實(shí)就是偏向鎖中保存當(dāng)前mark word字段使用的
偏向鎖加鎖
  • 偏向鎖入口函數(shù)_monitorenter字節(jié)碼
CASE(_monitorenter): {
    //當(dāng)前鎖對象
    oop lockee = STACK_OBJECT(-1);

    BasicObjectLock* limit = istate->monitor_base();
    BasicObjectLock* most_recent = (BasicObjectLock*) istate->stack_base();
    BasicObjectLock* entry = NULL;
    while (most_recent != limit ) {
      if (most_recent->obj() == NULL) entry = most_recent;
      else if (most_recent->obj() == lockee) break;
      most_recent++;
    }
    if (entry != NULL) { //找到entry即可用的最高位的Lock Record
      entry->set_obj(lockee); //Lock Record的obj指向鎖對象
      int success = false; //標(biāo)記位: 用于確定是否需要升級鎖為輕量級鎖
      uintptr_t epoch_mask_in_place = (uintptr_t)markOopDesc::epoch_mask_in_place;

      markOop mark = lockee->mark();
      intptr_t hash = (intptr_t) markOopDesc::no_hash;
      /**
      1. 可以使用偏向鎖
      */
      if (mark->has_bias_pattern()) {
        uintptr_t thread_ident;
        uintptr_t anticipated_bias_locking_value;
        thread_ident = (uintptr_t)istate->thread();
        anticipated_bias_locking_value =
          (((uintptr_t)lockee->klass()->prototype_header() | thread_ident) ^ (uintptr_t)mark) &
          ~((uintptr_t) markOopDesc::age_mask_in_place);
        /**
        * 1.2 : 當(dāng)前偏向線程是自己茄猫,什么都不做,設(shè)置success = true困肩,防止進(jìn)入下方鎖升級
        */
        if  (anticipated_bias_locking_value == 0) {
          // already biased towards this thread, nothing to do
          if (PrintBiasedLockingStatistics) {
            (* BiasedLocking::biased_lock_entry_count_addr())++;
          }
          success = true;
        }
        //批量重偏向 klass()->prototype_header不支持偏向鎖
        else if ((anticipated_bias_locking_value & markOopDesc::biased_lock_mask_in_place) != 0) {
          // try revoke bias
          markOop header = lockee->klass()->prototype_header();
          if (hash != markOopDesc::no_hash) {
            header = header->copy_set_hash(hash);
          }
          if (Atomic::cmpxchg_ptr(header, lockee->mark_addr(), mark) == mark) {
            if (PrintBiasedLockingStatistics)
              (*BiasedLocking::revoked_lock_entry_count_addr())++;
          }
        }
        /**
        * 1.3 當(dāng)前偏向鎖的epoch失效划纽,則重新偏向
        */
        else if ((anticipated_bias_locking_value & epoch_mask_in_place) !=0) {
          // 1.3.1 構(gòu)造一個(gè)偏向當(dāng)前線程切epoch為class最新的mark word
          markOop new_header = (markOop) ( (intptr_t) lockee->klass()->prototype_header() | thread_ident);
          if (hash != markOopDesc::no_hash) {
            new_header = new_header->copy_set_hash(hash);
          }
          // 1.3.2 CAS 替換 mark word
          if (Atomic::cmpxchg_ptr((void*)new_header, lockee->mark_addr(), mark) == mark) {
            if (PrintBiasedLockingStatistics)
              (* BiasedLocking::rebiased_lock_entry_count_addr())++;
          }
          else {
            /**
            * /替換ThreadID失敗,有兩種可能:
                1. 撤銷原有偏向ID锌畸,重新設(shè)置
                2. 有競爭勇劣,升級鎖,當(dāng)下方success=true 所以在monitorenter中升級的
            */
            CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
          }
          success = true;
        }
        else {
          /**
          * 1.1 當(dāng)前無鎖或 1.3 當(dāng)前偏向其他線程
          */
          markOop header = (markOop) ((uintptr_t) mark & ((uintptr_t)markOopDesc::biased_lock_mask_in_place |
                                                          (uintptr_t)markOopDesc::age_mask_in_place |
                                                          epoch_mask_in_place));
          if (hash != markOopDesc::no_hash) {
            header = header->copy_set_hash(hash);
          }
          /**
          * 1. 構(gòu)造一個(gè)偏向當(dāng)前線程的mark Word
            2. 設(shè)置首位的Lock Record lock即BasicLock為線程ID默認(rèn) 0xdeaddead
            3. 使用CAS替換操作
          */
          markOop new_header = (markOop) ((uintptr_t) header | thread_ident);
          // debugging hint
          DEBUG_ONLY(entry->lock()->set_displaced_header((markOop) (uintptr_t) 0xdeaddead);)
          if (Atomic::cmpxchg_ptr((void*)new_header, lockee->mark_addr(), header) == header) {
            if (PrintBiasedLockingStatistics)
              (* BiasedLocking::anonymously_biased_lock_entry_count_addr())++;
          }
          else {

            /**
            * 替換ThreadID失敗,有兩種可能:
               1. 撤銷原有偏向ID比默,重新設(shè)置
               2. 有競爭幻捏,升級鎖,當(dāng)下方success=true 所以在monitorenter中升級的
            */
            CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
          }
          success = true;
        }
      }

  • 偏向鎖示意圖:


    image
  • 整個(gè)過程示意圖:


    image
偏向鎖撤銷
  • 在獲取偏向鎖的過程因?yàn)椴粷M足條件導(dǎo)致要將鎖對象改為非偏向鎖狀態(tài)
  • 調(diào)取過程在 InterpreterRuntime::monitorenter -> 開啟JVM偏向鎖 ObjectSynchronizer::fast_enter -> java線程 BiasedLocking::revoke_and_rebias
static BiasedLocking::Condition revoke_bias(oop obj, bool allow_rebias, bool is_bulk, JavaThread* requesting_thread, JavaThread** biased_locker) {
  markOop mark = obj->mark();
  //如果不是偏向模式命咐,返回
  if (!mark->has_bias_pattern()) {
    return BiasedLocking::NOT_BIASED;
  }
  //創(chuàng)建兩個(gè)mark word篡九,一個(gè)是匿名偏向模式(101),一個(gè)是無鎖模式(001)
  uint age = mark->age();
  markOop   biased_prototype = markOopDesc::biased_locking_prototype()->set_age(age);
  markOop unbiased_prototype = markOopDesc::prototype()->set_age(age);
  ...

  JavaThread* biased_thread = mark->biased_locker();
  if (biased_thread == NULL) {
    //  匿名偏向醋奠。當(dāng)調(diào)用鎖對象的hashcode()方法可能會導(dǎo)致走到這個(gè)邏輯
    // 如果不允許重偏向榛臼,則將對象的mark word設(shè)置為無鎖模式
    if (!allow_rebias) {
      obj->set_mark(unbiased_prototype);
    }
    return BiasedLocking::BIAS_REVOKED;
  }

  //0. 判斷偏向線程是否還存活
  bool thread_is_alive = false;
  //0.1 如果當(dāng)前線程就是偏向線程
  if (requesting_thread == biased_thread) {
    thread_is_alive = true;
  } else {
    // 0.2 遍歷當(dāng)前jvm的所有線程,如果能找到窜司,則說明偏向的線程還存活
    for (JavaThread* cur_thread = Threads::first(); cur_thread != NULL; cur_thread = cur_thread->next()) {
      if (cur_thread == biased_thread) {
        thread_is_alive = true; //偏向線程還在
        break;
      }
    }
  }
  /**
  * 1. 如果偏向線程不存在了
  */
  if (!thread_is_alive) {
    // 1.1 允許重偏向則將對象mark word設(shè)置為匿名偏向狀態(tài)101沛善,否則設(shè)置為無鎖狀態(tài)001
    if (allow_rebias) {
      obj->set_mark(biased_prototype);
    } else {
      obj->set_mark(unbiased_prototype);
    }
    
    return BiasedLocking::BIAS_REVOKED;
  }

  /**
  * 2. 如果偏向線程存在,則遍歷該線程棧中所有的Lock Record
  */
  GrowableArray<MonitorInfo*>* cached_monitor_info = get_or_compute_monitor_info(biased_thread); //獲取線程中的棧 All Lock Record
  BasicLock* highest_lock = NULL;
  for (int i = 0; i < cached_monitor_info->length(); i++) { //從上向下遍歷
    MonitorInfo* mon_info = cached_monitor_info->at(i);
        /**
        * 2.1 判斷當(dāng)前Lock Record 的 owner是否指向鎖對象塞祈,如果指向說明當(dāng)前線程在偏向金刁,這個(gè)時(shí)候需要升級為輕量級鎖啦
        */
      if (TraceBiasedLocking && Verbose) {
        tty->print_cr("   mon_info->owner (" PTR_FORMAT ") == obj (" PTR_FORMAT ")",
                      p2i((void *) mon_info->owner()),
                      p2i((void *) obj));
      }
      /** 2.2 低位lock  直接修改偏向線程棧中的Lock Record。為了處理鎖重入的case织咧,在這里將Lock Record的Displaced Mark Word設(shè)置為null胀葱,
      * 第一個(gè)Lock Record會在下面的代碼中再處理
      */
      markOop mark = markOopDesc::encode((BasicLock*) NULL);
      highest_lock = mon_info->lock(); //
      highest_lock->set_displaced_header(mark);
    } else {
      ...
    }
  }
  /**
  *  2.3 對高位進(jìn)行處理,此時(shí)Displaced Mark Word即lock不能在設(shè)置成null
  */
  if (highest_lock != NULL) {
    // 2.3.1 修改第一個(gè)Lock Record為unbiased_prototype無鎖狀態(tài)笙蒙,
    highest_lock->set_displaced_header(unbiased_prototype);
    //2.3.2 將鎖對象obj obj的mark word設(shè)置為指向該Lock Record的指針
    obj->release_set_mark(markOopDesc::encode(highest_lock));
    assert(!obj->mark()->has_bias_pattern(), "illegal mark state: stack lock used bias bit");
    if (TraceBiasedLocking && (Verbose || !is_bulk)) {
      tty->print_cr("  Revoked bias of currently-locked object");
    }
  } else {

    /**
    * 3. 走到這里說明偏向線程已經(jīng)不在同步塊中了
    */
    if (allow_rebias) {
      //3.1 設(shè)置為匿名偏向狀態(tài)
      obj->set_mark(biased_prototype);
    } else {
      // 3.2 設(shè)置成無鎖狀態(tài)
      obj->set_mark(unbiased_prototype);
    }
  }
  1. 查看偏向的線程是否存活抵屿,如果已經(jīng)不存活了,則直接撤銷偏向鎖捅位。JVM維護(hù)了一個(gè)集合存放所有存活的線程轧葛,通過遍歷該集合判斷某個(gè)線程是否存活。
  2. 偏向的線程是否還在同步塊中艇搀,如果不在了尿扯,則撤銷偏向鎖。我們回顧一下偏向鎖的加鎖流程:每次進(jìn)入同步塊(即執(zhí)行monitorenter)的時(shí)候都會以從高往低的順序在棧中找到第一個(gè)可用的Lock Record焰雕,將其obj字段指向鎖對象衷笋。每次解鎖(即執(zhí)行monitorexit)的時(shí)候都會將最低的一個(gè)相關(guān)Lock Record移除掉。所以可以通過遍歷線程棧中的Lock Record來判斷線程是否還在同步塊中矩屁。
  3. 將偏向線程所有相關(guān)Lock Record的Displaced Mark Word設(shè)置為null辟宗,然后將最高位的Lock Record的Displaced Mark Word 設(shè)置為無鎖狀態(tài),最高位的Lock Record也就是第一次獲得鎖時(shí)的Lock Record(這里的第一次是指重入獲取鎖時(shí)的第一次)吝秕,然后將對象頭指向最高位的Lock Record泊脐,這里不需要用CAS指令,因?yàn)槭窃趕afepoint烁峭。 執(zhí)行完后容客,就升級成了輕量級鎖秕铛。原偏向線程的所有Lock Record都已經(jīng)變成輕量級鎖的狀態(tài)。
偏向鎖的釋放
  • 釋放過程相對比較簡單
CASE(_monitorexit): {
  oop lockee = STACK_OBJECT(-1);
  CHECK_NULL(lockee);
  // derefing's lockee ought to provoke implicit null check
  // find our monitor slot
  BasicObjectLock* limit = istate->monitor_base();
  BasicObjectLock* most_recent = (BasicObjectLock*) istate->stack_base();
  // 從低往高遍歷棧的Lock Record
  while (most_recent != limit ) {
    // 如果Lock Record關(guān)聯(lián)的是該鎖對象
    if ((most_recent)->obj() == lockee) {
      BasicLock* lock = most_recent->lock();
      markOop header = lock->displaced_header();
      // 釋放Lock Record
      most_recent->set_obj(NULL);
      // 如果是偏向模式缩挑,僅僅釋放Lock Record就好了但两。否則要走輕量級鎖or重量級鎖的釋放流程
      if (!lockee->mark()->has_bias_pattern()) {
        bool call_vm = UseHeavyMonitors;
        // header!=NULL說明不是重入,則需要將Displaced Mark Word CAS到對象頭的Mark Word
        if (header != NULL || call_vm) {
          if (call_vm || Atomic::cmpxchg_ptr(header, lockee->mark_addr(), lock) != lock) {
            // CAS失敗或者是重量級鎖則會走到這里调煎,先將obj還原镜遣,然后調(diào)用monitorexit方法
            most_recent->set_obj(lockee);
            CALL_VM(InterpreterRuntime::monitorexit(THREAD, most_recent), handle_exception);
          }
        }
      }
      //執(zhí)行下一條命令
      UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
    }
    //處理下一條Lock Record
    most_recent++;
  }
  // Need to throw illegal monitor state exception
  CALL_VM(InterpreterRuntime::throw_illegal_monitor_state_exception(THREAD), handle_exception);
  ShouldNotReachHere();
}
  1. 偏向鎖只有遇到其他線程嘗試競爭偏向鎖時(shí),持有偏向鎖的線程才會釋放鎖士袄,線程不會主動去釋放偏向鎖悲关。
  2. 偏向鎖的撤銷,需要等待全局安全點(diǎn)娄柳,它會首先暫停擁有偏向鎖的線程寓辱,判斷鎖對象是否處于被鎖定狀態(tài),撤銷偏向鎖后恢復(fù)到未鎖定(標(biāo)志位為“01”)或輕量級鎖(標(biāo)志位為“00”)的狀態(tài)
    • -XX:+PrintGCApplicationStoppedTime -XX:+PrintSafepointStatistics -XX:PrintSafepointStatisticsCount=1 查看停頓時(shí)間詳細(xì)信息
  3. JDK1.6以后默認(rèn)開啟偏向鎖UseBiasedLocking赤拒,對于高并發(fā)提升效率-XX:-UseBiasedLocking

輕量級鎖

輕量級鎖加鎖
  • 入口函數(shù)同上
    CASE(_monitorenter): {
      oop lockee = STACK_OBJECT(-1);
      ...
      if (entry != NULL) {
       ...
       // 上面省略的代碼中如果CAS操作失敗也會調(diào)用到InterpreterRuntime::monitorenter
    
        if (!success) {
          // 構(gòu)建一個(gè)無鎖狀態(tài)的Displaced Mark Word
          markOop displaced = lockee->mark()->set_unlocked();
          // 設(shè)置到Lock Record中去
          entry->lock()->set_displaced_header(displaced);
          bool call_vm = UseHeavyMonitors;
          if (call_vm || Atomic::cmpxchg_ptr(entry, lockee->mark_addr(), displaced) != displaced) {
            // 如果CAS替換不成功秫筏,代表鎖對象不是無鎖狀態(tài),這時(shí)候判斷下是不是鎖重入
            // Is it simple recursive case?
            if (!call_vm && THREAD->is_lock_owned((address) displaced->clear_lock_bits())) {
              entry->lock()->set_displaced_header(NULL);
            } else {
              // CAS操作失敗則調(diào)用monitorenter
              CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
            }
          }
        }
        UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
      } else {
        istate->set_msg(more_monitors);
        UPDATE_PC_AND_RETURN(0); // Re-execute
      }
    }
    
  1. 構(gòu)建一個(gè)無鎖的mark Work 設(shè)置到Lock Record 中l(wèi)ock中去
  2. CAS替換鎖對象的Mark word為指向1中的Lock Record挎挖,成功輕量級鎖完成
  3. 如果失敗这敬,兩種情況
    1. 是否是鎖重入,如果是則設(shè)置lock為null
    2. 否則蕉朵,調(diào)取monitorenter 自旋鎖或者升級鎖
  • 過程示意圖


    image
輕量級解鎖
  1. 解鎖過程由下往上查找當(dāng)前線程的Lock Record 中owner為鎖對象后判斷BasicLock
    1. 如果BasicLock == null ,表示一個(gè)鎖重入崔涂,當(dāng)前線程還在同步塊中,不做操作
    2. 如果BasicLock有值始衅,即為鎖對象指向的Display Mark Word 冷蚂,則表示這時(shí)候需要真正釋放鎖,將DisPlay Mark Word替換回鎖對象的Mark Word中去汛闸,如果失敗蝙茶,則調(diào)用InterpreterRuntime::monitorexit
  2. 總結(jié):輕量級鎖解鎖時(shí),把復(fù)制的對象頭替換回去(cas)如果替換成功(就是要把無所的狀態(tài)放回去給對象頭诸老,之后鎖繼續(xù)被拿還是輕量級鎖隆夯,但是如果鎖已經(jīng)是重量級鎖了,那么就失敗别伏,之后鎖就是重量級的鎖了)吮廉,鎖結(jié)束,之后別的線程來拿還是輕量級鎖畸肆,如果失敗,說明已有競爭宙址,釋放鎖轴脐,此時(shí)把對象頭設(shè)為重量級鎖,并notify 喚醒其他等待線程。

重量級鎖

重量級鎖加鎖
  1. 入口函數(shù)大咱,當(dāng)存在競爭時(shí)恬涧,調(diào)用ObjectSynchronizer::slow_enter() 最后一行
  //需要膨脹為重量級鎖,膨脹前碴巾,設(shè)置Displaced Mark Word為一個(gè)特殊值溯捆,代表該鎖正在用一個(gè)重量級鎖的monitor
  lock->set_displaced_header(markOopDesc::unused_mark());
  //先調(diào)用inflate膨脹為重量級鎖,該方法返回一個(gè)ObjectMonitor對象厦瓢,然后調(diào)用其enter方法
  ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD);
  1. 鎖膨脹提揍,即獲取一個(gè)ObjectMonitor對象inflate()
//結(jié)構(gòu)體如下objectMonitor.hpp中定義
ObjectMonitor::ObjectMonitor() {  
  _header       = NULL;   //對象頭的Mark Word
  _count       = 0;   
  _waiters      = 0,   
  _recursions   = 0;       //線程的重入次數(shù)
  _object       = NULL;  
  _owner        = NULL;    //標(biāo)識擁有該monitor的線程
  _WaitSet      = NULL;    //等待線程組成的雙向循環(huán)鏈表,_WaitSet是第一個(gè)節(jié)點(diǎn)
  _WaitSetLock  = 0 ;  
  _Responsible  = NULL ;  
  _succ         = NULL ;  
  _cxq          = NULL ;    //多線程競爭鎖進(jìn)入時(shí)的單向鏈表
  FreeNext      = NULL ;  
  _EntryList    = NULL ;    //_owner從該雙向循環(huán)鏈表中喚醒線程結(jié)點(diǎn)煮仇,_EntryList是第一個(gè)節(jié)點(diǎn)
  _SpinFreq     = 0 ;  
  _SpinClock    = 0 ;  
  OwnerIsThread = 0 ;  
} 
inflate膨脹
  • inflate膨脹有四種情況:
    1. Inflated(重量級鎖狀態(tài)) - 直接返回
    2. INFLATING(膨脹中) - 忙等待直到膨脹完成
    3. Stack-locked(輕量級鎖狀態(tài)) - 膨脹
    4. Neutral(無鎖狀態(tài)) - 膨脹
  1. Inflated 重量級鎖狀態(tài): 直接返回
  /**
  * 1. 已經(jīng)是重量級鎖了劳跃,直接返回obj ->mark word指向的monitor對象
  */
  if (mark->has_monitor()) {
      ObjectMonitor * inf = mark->monitor() ;
      return inf ;
  }
  1. INFLATING 其他線程正在膨脹中,則當(dāng)前線程等待
  /**
  * 2. 有其他線程正在膨脹中浙垫,這個(gè)時(shí)候忙等待后continue
  */
  if (mark == markOopDesc::INFLATING()) {
     TEVENT (Inflate: spin while INFLATING) ;
     //3.1 在該方法中會進(jìn)行spin/yield/park等操作完成自旋動作
     ReadStableMark(object) ;
     continue ;
  }
  1. Stack-locked(輕量級鎖狀態(tài)升級) - 膨脹
/**
* 3. 輕量級鎖升級
*/
if (mark->has_locker()) {
   // 3.1 omAlloc從當(dāng)前線程的Self->omFreeList的 ObjectMonitor數(shù)組中獲取刨仑,成功加入全局中管理,否則從全局中獲取
  ObjectMonitor * m = omAlloc (Self) ;
  // 初始化ObjectMonitor對象
  m->Recycle();
  m->_Responsible  = NULL ;
  m->OwnerIsThread = 0 ;
  m->_recursions   = 0 ;
  m->_SpinDuration = ObjectMonitor::Knob_SpinLimit ;   // Consider: maintain by type/class
  // 將鎖對象的mark word設(shè)置為INFLATING (0)狀態(tài)
  markOop cmp = (markOop) Atomic::cmpxchg_ptr (markOopDesc::INFLATING(), object->mark_addr(), mark) ;
  if (cmp != mark) {
     omRelease (Self, m, true) ;
     continue ;       // Interference -- just retry
  }

  // 棧中的displaced mark word
  markOop dmw = mark->displaced_mark_helper() ;

  // monitor中的header設(shè)置為Displaced mark Word
  m->set_header(dmw) ;

  //將原有的Lock Record設(shè)置給monitor的owner
  m->set_owner(mark->locker());
  //monitor的obj設(shè)置鎖對象
  m->set_object(object);
  
  //將鎖對象頭設(shè)置為重量級鎖狀態(tài)
  object->release_set_mark(markOopDesc::encode(m));

  ...
  return m ;
}
  1. 無鎖狀態(tài)
 /**
  * 4. 無鎖狀態(tài)
  */
  //獲取一個(gè)ObjectMonitor對象
  ObjectMonitor * m = omAlloc (Self) ;
  // 初始化操作
  m->Recycle();
  m->set_header(mark);  //設(shè)置header為mark Word
  m->set_owner(NULL); //Lock Record不存在
  m->set_object(object);
  m->OwnerIsThread = 1 ;
  m->_recursions   = 0 ;
  m->_Responsible  = NULL ;
  m->_SpinDuration = ObjectMonitor::Knob_SpinLimit ;       // consider: keep metastats by type/class
    //用CAS替換對象頭的mark word為重量級鎖狀態(tài)
  if (Atomic::cmpxchg_ptr (markOopDesc::encode(m), object->mark_addr(), mark) != mark) {
      m->set_object (NULL) ;
      m->set_owner  (NULL) ;
      m->OwnerIsThread = 0 ;
      m->Recycle() ;
      omRelease (Self, m, true) ;
      m = NULL ;
      continue ;
  }
  
  ...
  return m ;
}
獲取鎖->enter(THREAD)
  1. 在ObjectMonitor::enter()四種情況
    1. 無鎖直接可以獲取到
    2. 重入情況
    3. 輕量級升級情況
    4. 重量級鎖競爭情況
void ATTR ObjectMonitor::enter(TRAPS) {
   
  Thread * const Self = THREAD ;
  void * cur ;
  /**
  * 1. owner為null代表無鎖狀態(tài)夹姥,如果能CAS設(shè)置成功杉武,則當(dāng)前線程直接獲得鎖
  */
  cur = Atomic::cmpxchg_ptr (Self, &_owner, NULL) ;
  if (cur == NULL) {
     ...
     return ;
  }
  /**
  * 2. 如果是重入的情況
  */
  if (cur == Self) {
     // TODO-FIXME: check for integer overflow!  BUGID 6557169.
     _recursions ++ ;
     return ;
  }
  /**
  * 3. 當(dāng)前線程是之前持有輕量級鎖的線程。由輕量級鎖膨脹且第一次調(diào)用enter方
  * 法辙售,那cur是指向Lock Record的指針
  */
  if (Self->is_lock_owned ((address)cur)) {
    assert (_recursions == 0, "internal state error");
    // 重入計(jì)數(shù)重置為1
    _recursions = 1 ;
    // 設(shè)置owner字段為當(dāng)前線程(之前owner是指向Lock Record的指針)
    _owner = Self ;
    OwnerIsThread = 1 ;
    return ;
  }

  ...

  // 在調(diào)用系統(tǒng)的同步操作之前轻抱,先嘗試自旋獲得鎖
  if (Knob_SpinEarly && TrySpin (Self) > 0) {
     ...
     //自旋的過程中獲得了鎖,則直接返回
     Self->_Stalled = 0 ;
     return ;
  }

  ...

  { 
    ...

    /**
    * 4. 重量級鎖競爭情況
    */
    for (;;) {
      jt->set_suspend_equivalent();
      // 在該方法中調(diào)用系統(tǒng)同步操作圾亏,真正的競爭鎖
      EnterI (THREAD) ;
      ...
    }
    Self->set_current_pending_monitor(NULL);
    
  }

  ...

}
重量級鎖競爭
  • 首先分析objectMonitor中的元素: cxq(下圖中的ContentionList)十拣,EntryList ,WaitSet志鹃,owner夭问, 前三個(gè)是由ObjectWaiter的鏈表結(jié)構(gòu), owner是當(dāng)前持有鎖的線程


    image
  • ObjectWaiter結(jié)構(gòu)
class ObjectWaiter : public StackObj {
 public:
  enum TStates { TS_UNDEF, TS_READY, TS_RUN, TS_WAIT, TS_ENTER, TS_CXQ } ;
  enum Sorted  { PREPEND, APPEND, SORTED } ;
  ObjectWaiter * volatile _next;
  ObjectWaiter * volatile _prev;
  Thread*       _thread;
  jlong         _notifier_tid;
  ParkEvent *   _event;
  volatile int  _notified ;
  volatile TStates TState ;
  Sorted        _Sorted ;           // List placement disposition
  bool          _active ;           // Contention monitoring is enabled
 public:
  ObjectWaiter(Thread* thread); //對應(yīng)獲取鎖的線程

  void wait_reenter_begin(ObjectMonitor *mon);
  void wait_reenter_end(ObjectMonitor *mon);
};
  • 重量級加鎖過程
void ATTR ObjectMonitor::EnterI (TRAPS) {
    Thread * Self = THREAD ;
    ...
    // 嘗試獲得鎖
    if (TryLock (Self) > 0) {
        ...
        return ;
    }

    DeferredInitialize () ;
 
    // 自旋
    if (TrySpin (Self) > 0) {
        ...
        return ;
    }
    
    ...
    
    // 將線程封裝成node節(jié)點(diǎn)ObjectWaiter中
    ObjectWaiter node(Self) ;
    Self->_ParkEvent->reset() ;
    node._prev   = (ObjectWaiter *) 0xBAD ;
    node.TState  = ObjectWaiter::TS_CXQ ;

    /**
    * 1. 將node節(jié)點(diǎn)插入到_cxq隊(duì)列的頭部曹铃,cxq是一個(gè)單向鏈表
    */
    ObjectWaiter * nxt ;
    for (;;) {
        node._next = nxt = _cxq ;
        if (Atomic::cmpxchg_ptr (&node, &_cxq, nxt) == nxt) break ;

        // CAS失敗的話 再嘗試獲得鎖缰趋,這樣可以降低插入到_cxq隊(duì)列的頻率
        if (TryLock (Self) > 0) {
            ...
            return ;
        }
    }

    // SyncFlags默認(rèn)為0,如果沒有其他等待的線程,則將_Responsible設(shè)置為自己
    if ((SyncFlags & 16) == 0 && nxt == NULL && _EntryList == NULL) {
        Atomic::cmpxchg_ptr (Self, &_Responsible, NULL) ;
    }


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

    for (;;) {

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

        ...

        // park self
        if (_Responsible == Self || (SyncFlags & 1)) {
            // 當(dāng)前線程是_Responsible時(shí)涂滴,調(diào)用的是帶時(shí)間參數(shù)的park
            TEVENT (Inflated enter - park TIMED) ;
            Self->_ParkEvent->park ((jlong) RecheckInterval) ;
            // Increase the RecheckInterval, but clamp the value.
            RecheckInterval *= 8 ;
            if (RecheckInterval > 1000) RecheckInterval = 1000 ;
        } else {
            //否則直接調(diào)用park掛起當(dāng)前線程
            TEVENT (Inflated enter - park UNTIMED) ;
            Self->_ParkEvent->park() ;
        }

        if (TryLock(Self) > 0) break ;

        ...
        
        if ((Knob_SpinAfterFutile & 1) && TrySpin (Self) > 0) break ;

        ...
        /**
        * 2. 在當(dāng)前線程釋放鎖時(shí)奶卓,_succ會被設(shè)置為EntryList或_cxq中的一個(gè)線程:表示假定繼承人
        * _succ = Knob_SuccEnabled ? Wakee->_thread : NULL ; 在ExitEpilog中設(shè)置
        * ExitEpilog(w) 調(diào)用w = _cxq 或者 w = _EntryList 
        */
        if (_succ == Self) _succ = NULL ;

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

   // 走到這里說明已經(jīng)獲得鎖了

    assert (_owner == Self      , "invariant") ;
    assert (object() != NULL    , "invariant") ;
  
    // 將當(dāng)前線程的node從cxq或EntryList中移除
    UnlinkAfterAcquire (Self, &node) ;
    if (_succ == Self) _succ = NULL ;
    if (_Responsible == Self) {
        _Responsible = NULL ;
        OrderAccess::fence();
    }
    ...
    return ;
}
  1. 當(dāng)一個(gè)線程嘗試獲得鎖時(shí),如果該鎖已經(jīng)被占用灰粮,則會將該線程封裝成一個(gè)ObjectWaiter對象插入到cxq的隊(duì)列的隊(duì)首,然后調(diào)用park函數(shù)掛起當(dāng)前線程忍坷。
  2. 當(dāng)線程釋放鎖時(shí)粘舟,會從cxq或EntryList中挑選一個(gè)線程喚醒熔脂,被選中的線程叫做Heir presumptive即假定繼承人(Ready Thread),假定繼承人被喚醒后會嘗試獲得鎖柑肴,但synchronized是非公平的霞揉,所以假定繼承人不一定能獲得鎖;
  3. 如果線程獲得鎖后調(diào)用Object#wait方法,則會將線程加入到WaitSet中
  4. 當(dāng)被Object#notify喚醒后晰骑,會將線程從WaitSet移動到cxq或EntryList中去适秩。
  5. 調(diào)用一個(gè)鎖對象的wait或notify方法時(shí),如當(dāng)前鎖的狀態(tài)是偏向鎖或輕量級鎖則會先膨脹成重量級鎖硕舆。
重量級鎖解鎖
  • 解鎖包括: 1. 當(dāng)前是可重入鎖 2. 正常釋放鎖
  1. 可重入鎖
// 重入計(jì)數(shù)器還不為0秽荞,則計(jì)數(shù)器-1后返回
if (_recursions != 0) {
 _recursions--;        // this is simple recursive enter
 TEVENT (Inflated exit - recursive) ;
 return ;
}
  1. 正常釋放鎖,可能需要喚醒其他線程
if (Knob_ExitPolicy == 0) { //Knob_ExitPolicy默認(rèn)為0
     /**
     * 1. synchronized非公平鎖原因: 當(dāng)前線程先釋放鎖岗宣,這時(shí)如果有其他線程進(jìn)入同步塊則能獲得鎖蚂会,不管剛剛上面的
     假定繼承人及_succ是否存在,因此它是非公平鎖
     */
     OrderAccess::release_store_ptr (&_owner, NULL) ;   // drop the lock
     //內(nèi)存屏障耗式,是否需要喚醒繼任者
     OrderAccess::storeload() ;                         //See if we need to wake a successor 
     /**
     * 2. 如果沒有等待線程_EntryList和_cxq均為空胁住,或者_(dá)succ假定繼承人存在,這個(gè)時(shí)候是沒有競爭的刊咳,可以運(yùn)行彪见,不需要喚醒,直接返回就好了
     */
     if ((intptr_t(_EntryList)|intptr_t(_cxq)) == 0 || _succ != NULL) {
        TEVENT (Inflated exit - simple egress) ;
        return ;
     }
     TEVENT (Inflated exit - complex egress) ;
     
      /**
      * 3. 走到這里表示娱挨,當(dāng)前_EntryList 余指,_cxq不為空,且沒有假定繼承人跷坝,需要喚醒操作了
      * 而要執(zhí)行之后的操作就需要重新獲得鎖酵镜,即設(shè)置_owner為當(dāng)前線程
      */
     if (Atomic::cmpxchg_ptr (THREAD, &_owner, NULL) != NULL) {
        return ;
     }
     
    ...
    
    /**
    * 4. 根據(jù)不同的QMode執(zhí)行不同的喚醒策略,默認(rèn)為0
    */
    ObjectWaiter * w = NULL ;
      // code 4:根據(jù)QMode的不同會有不同的喚醒策略柴钻,默認(rèn)為0
      int QMode = Knob_QMode ;
      if (QMode == 2 && _cxq != NULL) {
          /**
          * QMode == 2 : cxq中的線程有更高優(yōu)先級淮韭,直接喚醒cxq的隊(duì)首線程,返回了贴届,最終喚醒的就是這個(gè)隊(duì)首線程_succ == w
          */
          w = _cxq ;
          assert (w != NULL, "invariant") ;
          assert (w->TState == ObjectWaiter::TS_CXQ, "Invariant") ;
          ExitEpilog (Self, w) ;
          return ;
      }
      f (QMode == 3 && _cxq != NULL) {
          /**
          * QMode == 3: 將cxq中的元素插入到EntryList的末尾,繼續(xù)向下執(zhí)行程序
          */
          ...
      }
      if (QMode == 4 && _cxq != NULL) {
          /**
          * QMode == 4: 將cxq插入到EntryList的隊(duì)首
          */
          ...
      }
      /**
      * 默認(rèn)QMode == 0 : 什么也不做
      */
      
      /**
      * 3. 如果_EntryList不為null ,取隊(duì)首w 靠粪,喚醒ObjectWaiter對象的線程,然后立即返回
      */
      w = _EntryList  ;
      if (w != NULL) {
          assert (w->TState == ObjectWaiter::TS_ENTER, "invariant") ;
          ExitEpilog (Self, w) ;
          return ;
      }

      /**
      * 4. 如果EntryList的首元素為空毫蚓,就將cxq的所有元素放入到EntryList中
      * 然后再從EntryList中取出來隊(duì)首元素執(zhí)行ExitEpilog方法占键,然后立即返回
      *
      * 使用w保存_cxq隊(duì)列后,將_cxq設(shè)置null
      */
      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") ;
          //下方用到w替代_cxq
          ObjectWaiter * u = (ObjectWaiter *) Atomic::cmpxchg_ptr (NULL, &_cxq, w) ;
          if (u == w) break ;
          w = u ;
      }
      
      if (QMode == 1) {
         // QMode == 1 : 將cxq中的元素轉(zhuǎn)移到EntryList元潘,并反轉(zhuǎn)順序
        ...
          
      } else {
         // QMode == 0 or QMode == 2‘
         // 將cxq中的元素轉(zhuǎn)移到EntryList
         _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 ;
         }
         
     //如果當(dāng)前有個(gè)新增的假定繼承人畔乙,所以不需要當(dāng)前線程去喚醒,以減少上下文切換的比率
      if (_succ != NULL) continue;
    /**
    * 4.1 將上方有cxq轉(zhuǎn)移到_EntryList首位喚醒即可
    */
      w = _EntryList  ;
      if (w != NULL) {
          guarantee (w->TState == ObjectWaiter::TS_ENTER, "invariant") ;
          ExitEpilog (Self, w) ;
          return ;
      }
      
      
//ExitEpilog用于喚醒線程操作
void ObjectMonitor::ExitEpilog (Thread * Self, ObjectWaiter * Wakee) {
   assert (_owner == Self, "invariant") ;
   /**
   * 假定繼承人的設(shè)置翩概,查看調(diào)用方法在解鎖exit中設(shè)置w = _EntryList
   * 只有QMode == 2 時(shí) w = _cxq 中取
   */
   _succ = Knob_SuccEnabled ? Wakee->_thread : NULL ;
   ParkEvent * Trigger = Wakee->_event ;
   Wakee  = NULL ;
   //如果有其他線程進(jìn)來啸澡,將會直接運(yùn)行袖订,不會喚醒操作了
   OrderAccess::release_store_ptr (&_owner, NULL) ;
   OrderAccess::fence() ;                            
   if (SafepointSynchronize::do_call_back()) {
      TEVENT (unpark before SAFEPOINT) ;
   }
   DTRACE_MONITOR_PROBE(contended__exit, this, object(), Self);
   Trigger->unpark() ; //喚醒線程
   // Maintain stats and report events to JVMTI
   if (ObjectMonitor::_sync_Parks != NULL) {
      ObjectMonitor::_sync_Parks->inc() ;
   }
}

  1. 釋放鎖,這個(gè)時(shí)刻其他的線程能獲取到鎖,synchronized是非公平鎖的原因
  2. 如果當(dāng)前沒有等待的線程或succ 嗅虏!= null 有一個(gè)假定繼承人存在(可以運(yùn)行它) ,則直接返回就好了上沐,因?yàn)椴恍枰獑拘哑渌€程皮服。
  3. 當(dāng)前線程重新獲得鎖,因?yàn)橹笠僮鱟xq和EntryList隊(duì)列以及喚醒線程
  4. 根據(jù)QMode的不同参咙,會執(zhí)行不同的喚醒策略: 默認(rèn)為0
    1. QMode = 2且cxq非空:取cxq隊(duì)列隊(duì)首的ObjectWaiter對象龄广,調(diào)用ExitEpilog方法,該方法會喚醒ObjectWaiter對象的線程蕴侧,然后立即返回择同,后面的代碼不會執(zhí)行了;
    2. QMode = 3且cxq非空:把cxq隊(duì)列插入到EntryList的尾部净宵;
    3. QMode = 4且cxq非空:把cxq隊(duì)列插入到EntryList的頭部敲才;
    4. QMode = 0:默認(rèn),暫時(shí)什么都不做择葡,繼續(xù)往下看紧武;
  5. 如果EntryList的首元素非空,就取出來調(diào)用ExitEpilog方法敏储,該方法會喚醒ObjectWaiter對象的線程阻星,然后立即返回;
  6. 如果EntryList的首元素為空已添,就將cxq的所有元素放入到EntryList中妥箕,然后再從EntryList中取出來隊(duì)首元素執(zhí)行ExitEpilog方法,然后立即返回更舞;
wait ,notify喚醒操作
  1. wait將當(dāng)前Thread構(gòu)造ObjectWaiter后通過AddWaiter加入_waitSet隊(duì)列
inline void ObjectMonitor::AddWaiter(ObjectWaiter* node) {

  // 從waitSet頭部添加
  if (_WaitSet == NULL) {
    _WaitSet = node;
    node->_prev = node;
    node->_next = node;
  } else {
    ObjectWaiter* head = _WaitSet ;
    ObjectWaiter* tail = head->_prev;
    assert(tail->_next == head, "invariant check");
    tail->_next = node;
    head->_prev = node;
    node->_next = head;
    node->_prev = tail;
  }
}
  1. notify喚醒wait
//獲取等待隊(duì)列中一個(gè)ObjectWaiter
ObjectWaiter * iterator = DequeueWaiter() ; //也是從頭部取值呀
inline ObjectWaiter* ObjectMonitor::DequeueWaiter() {
  // dequeue the very first waiter
  ObjectWaiter* waiter = _WaitSet;
  if (waiter) {
    DequeueSpecificWaiter(waiter);
  }
  return waiter;
}
  ...
 int Policy = Knob_MoveNotifyee ; //默認(rèn)為2
 
 ObjectWaiter * List = _EntryList ;
 if (Policy == 2) {      // prepend to cxq
     // 如果_EntryList為null,將喚醒的加入_EntryList很大幾率運(yùn)行
     if (List == NULL) {
         iterator->_next = iterator->_prev = NULL ;
         _EntryList = iterator ;
     } else { //否則加入_cxq隊(duì)首畦幢,等待_EntryList為null后加入運(yùn)行,QMode =默認(rèn)0時(shí)
        iterator->TState = ObjectWaiter::TS_CXQ ;
        for (;;) {
            ObjectWaiter * Front = _cxq ;
            iterator->_next = Front ;
            if (Atomic::cmpxchg_ptr (iterator, &_cxq, Front) == Front) {
                break ;
            }
        }
     }
}
  • 加入等待隊(duì)列從隊(duì)首添加疏哗,取出也是從隊(duì)首呛讲,notify只喚醒一個(gè)添加到_cxq或者_(dá)EntryList中,notifyAll()邏輯一致返奉,只是使用for循環(huán)獲取waitSet中所有數(shù)據(jù)添加而已

思考

  • 對于下方代碼贝搁,請您分析其執(zhí)行順序:
public class SyncDemo {
    public static void main(String[] args) throws InterruptedException {
        SyncDemo syncDemo = new SyncDemo();
        syncDemo.startThreadA();
        Thread.sleep(100);
        syncDemo.startThreadB();
        Thread.sleep(100);
        syncDemo.startThreadC();
    }

    final Object lock = new Object();


    public void startThreadA() {
        new Thread(() -> {
            synchronized (lock) {
                System.out.println("ThreadA get lock");
                try {
                    Thread.sleep(500);
                } catch (Exception e) {}
                System.out.println("ThreadA release lock");
            }
        }, "thread-A").start();
    }

    public void startThreadB() {
        new Thread(() -> {
            synchronized (lock) {
                System.out.println("ThreadB get lock");
            }
        }, "thread-B").start();
    }

    public void startThreadC() {
        new Thread(() -> {
            synchronized (lock) {
                System.out.println("ThreadC get lock");
            }
        }, "thread-C").start();
    }
}
//最終打印結(jié)果一定是 A -> C -> B

下載鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市芽偏,隨后出現(xiàn)的幾起案子雷逆,更是在濱河造成了極大的恐慌,老刑警劉巖污尉,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件膀哲,死亡現(xiàn)場離奇詭異往产,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)某宪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門仿村,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人兴喂,你說我怎么就攤上這事蔼囊。” “怎么了衣迷?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵畏鼓,是天一觀的道長。 經(jīng)常有香客問我壶谒,道長云矫,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任汗菜,我火速辦了婚禮让禀,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘呵俏。我一直安慰自己堆缘,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布普碎。 她就那樣靜靜地躺著吼肥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪麻车。 梳的紋絲不亂的頭發(fā)上缀皱,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機(jī)與錄音动猬,去河邊找鬼啤斗。 笑死,一個(gè)胖子當(dāng)著我的面吹牛赁咙,可吹牛的內(nèi)容都是我干的钮莲。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼彼水,長吁一口氣:“原來是場噩夢啊……” “哼崔拥!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起凤覆,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤链瓦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體慈俯,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡渤刃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了贴膘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片卖子。...
    茶點(diǎn)故事閱讀 38,161評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖刑峡,靈堂內(nèi)的尸體忽然破棺而出揪胃,到底是詐尸還是另有隱情,我是刑警寧澤氛琢,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站随闪,受9級特大地震影響阳似,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜铐伴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一撮奏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧当宴,春花似錦畜吊、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至梯浪,卻和暖如春捌年,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背挂洛。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工礼预, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人虏劲。 一個(gè)月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓托酸,卻偏偏與公主長得像,于是被迫代替她去往敵國和親柒巫。 傳聞我的和親對象是個(gè)殘疾皇子励堡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評論 2 344

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