源碼解析-線程A請求偏向於線程B的偏向鎖

一、先理理所有情況

假設偏向鎖有效,即類沒有關閉偏向模式,且其 epoch 沒有過期。
則一定會發生偏向鎖的撤銷。
第一類情況:線程 B 沒有正在持有該偏向鎖。

  1. 僅發生單個偏向撤銷,偏向鎖先被撤銷爲無鎖,然後在 slow_enter 裏升級爲輕量級鎖。
  2. 觸發了批量重偏向,偏向鎖先被撤銷爲匿名偏向,隨後立即重偏向於 A。
  3. 觸發了批量撤銷,偏向鎖同樣先被撤銷爲無鎖,然後在 slow_enter 裏升級爲輕量級鎖。

第二類情況:線程 B 正在持有該偏向鎖。

  1. 僅發生單個偏向撤銷,偏向鎖撤銷爲 B 的輕量級鎖,然後在 slow_enter 裏膨脹爲重量級鎖。
  2. 觸發了批量重偏向,偏向鎖撤銷爲 B 的輕量級鎖,然後在 slow_enter 裏膨脹爲重量級鎖。
  3. 觸發了批量撤銷,偏向鎖撤銷爲 B 的輕量級鎖,然後在 slow_enter 裏膨脹爲重量級鎖。

情況貌似不止這 6 種,因爲可能在撤銷時,B 正在持有該偏向鎖,但是當撤銷成功後(撤銷過程由 VM 線程在安全點執行,此時 B 被阻塞),它可能立即退出了同步代碼塊,並把鎖從輕量級鎖還原成了無鎖,此時 A 可能是加輕量級鎖成功,也可能是加重量級鎖成功,取決於 A 接下來的加鎖過程中,B 釋放鎖的時機。
還有第一類情況中,撤銷爲無鎖,和之後的加輕量級鎖也是分離的,可能會加鎖失敗,然後膨脹爲重量級鎖。都有可能。

上面所有情況都可以在源碼裏找到對應的處理邏輯。但後面我只會點出前面 6 種對應源碼的處理,其餘情況都可以類似分析可得,不再贅述。

批量重偏向和批量撤銷的觸發條件,可以參考:源碼解析-觸發批量撤銷或批量重偏向的條件

二、從前到後走一遍源碼

HotSpot 有 字節碼解釋器(bytecodeInterpreter) 和 模板解釋器(templateInterpreter),前者是 C++ 實現,後者是彙編實現。
HotSpot 實際使用的是 模板解釋器,沒有用 字節碼解釋器。但兩者邏輯類似,下面從可讀性上考慮,給出 字節碼解釋器 montorenter 指令的解析源碼(說的我好像會彙編一樣,emm):
源碼位置:bytecodeInterpreter.cpp#1816

  CASE(_monitorenter): {
	oop lockee = STACK_OBJECT(-1);
	// derefing's lockee ought to provoke implicit null check
	CHECK_NULL(lockee); // lockee 就是鎖對象
	// find a free monitor or one already allocated for this object
	// if we find a matching object then we need a new monitor
	// since this is recursive enter
	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->set_obj(lockee);
	  int success = false; // 標誌變量
	  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;
	  // implies UseBiasedLocking
	  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);

		if  (anticipated_bias_locking_value == 0) {
		  // already biased towards this thread, nothing to do
          // case 1:已經偏向於自身,什麼都不做。。。。。。。。。。。。。。。。。。
		  if (PrintBiasedLockingStatistics) {
			(* BiasedLocking::biased_lock_entry_count_addr())++;
		  }
		  success = true;
		}
		else if ((anticipated_bias_locking_value & markOopDesc::biased_lock_mask_in_place) != 0) {
		  // try revoke bias
          // case 2:試圖撤銷偏向(批量撤銷的漏網之魚)。。。。。。。。。。。。。。。
          // 只有這種情況,success 沒有賦爲 true
          // 因爲這種情況是 類元數據(klass)禁用了可偏向屬性,即該類發生了批量撤銷
          // 對於這種情況,放到後面加輕量級鎖
		  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())++;
		  }
		}
		else if ((anticipated_bias_locking_value & epoch_mask_in_place) !=0) {
		  // try rebias
          // case 3:嘗試重偏向。。。。。。。。。。。。。。。。。。。。。。。。。。
          // 這種情況是因爲,對象的 epoch 和 klass 的 epoch 不等
		  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);
		  }
		  if (Atomic::cmpxchg_ptr((void*)new_header, lockee->mark_addr(), mark) == mark) {
			if (PrintBiasedLockingStatistics)
			  (* BiasedLocking::rebiased_lock_entry_count_addr())++;
		  }
		  else {
            // 重偏向失敗,說明有另一個線程重偏向成功,需要撤銷偏向,走下面這個調用
			CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
		  }
		  success = true;
		}
		else {
		  // try to bias towards thread in case object is anonymously biased
          // case 4:把鎖狀態當成是匿名偏向處理,嘗試偏向於自身。。。。。。。。。。。。
		  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);
		  }
		  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 {
            // 嘗試失敗,兩種可能
            // 一是:本來是匿名偏向,被別的線程偏向成功,自己失敗了,需要撤銷偏向
            // 二是:本來就不是匿名偏向,而是已經偏向於別的線程,還是需要撤銷偏向
            // 撤銷偏向走下面這個調用
			CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
		  }
		  success = true;
		}
	  }

	  // traditional lightweight locking
	  if (!success) { // 走到這不管三七二十一,先加輕量級鎖
		markOop displaced = lockee->mark()->set_unlocked();
		entry->lock()->set_displaced_header(displaced);
		bool call_vm = UseHeavyMonitors;
		if (call_vm || Atomic::cmpxchg_ptr(entry, lockee->mark_addr(), displaced) != displaced) {
		  // Is it simple recursive case?
		  if (!call_vm && THREAD->is_lock_owned((address) displaced->clear_lock_bits())) {
			entry->lock()->set_displaced_header(NULL); // 處理輕量級鎖重入的情況
		  } else {
            // 加輕量級鎖失敗,原因有很多
            // 可能是,無鎖加輕量級鎖時,被別的線程先加成功
            // 或者,本來就是別人持有的輕量級鎖
            // 或者,是重量級鎖
            // 還是走下面這個調用處理
			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
	}
  }

線程請求偏向於另一個線程的偏向鎖,對應 case 4。

會走到這個邏輯:InterpreterRuntime::monitorenter

IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* thread, BasicObjectLock* elem))
  ...
  Handle h_obj(thread, elem->obj());
  ...
  if (UseBiasedLocking) { // UseBiasedLocking 參數是默認開啓的
    // 一般走這裏
    ObjectSynchronizer::fast_enter(h_obj, elem->lock(), true, CHECK);
  } else {
    ObjectSynchronizer::slow_enter(h_obj, elem->lock(), CHECK);
  }
  ...

接着走到 ObjectSynchronizer::fast_enter

// 從 InterpreterRuntime::monitorenter,傳入第三個參數 attempt_rebias 爲 true
void ObjectSynchronizer::fast_enter(Handle obj, BasicLock* lock, bool attempt_rebias, TRAPS) {
 if (UseBiasedLocking) {
    if (!SafepointSynchronize::is_at_safepoint()) {
      // 執行 fast_enter 時不在安全點,一般走這裏
      BiasedLocking::Condition cond = BiasedLocking::revoke_and_rebias(obj, attempt_rebias, THREAD);
      if (cond == BiasedLocking::BIAS_REVOKED_AND_REBIASED) {
        return; // 第一類情況裏的第二種會在這裏直接返回,不會走到後面的 slow_enter
      }
    } else {
      // 執行 fast_enter 時在安全點
      ...
      BiasedLocking::revoke_at_safepoint(obj);
    }
    ...
 }

 slow_enter (obj, lock, THREAD) ;
}

接下來,先是走到 BiasedLocking::revoke_and_rebias

// 此時,第二個參數 attempt_rebias 爲 true
BiasedLocking::Condition BiasedLocking::revoke_and_rebias(Handle obj, bool attempt_rebias, TRAPS) {
  assert(!SafepointSynchronize::is_at_safepoint(), "must not be called while at safepoint");

  markOop mark = obj->mark(); // 對象 mark word
  if (mark->is_biased_anonymously() && !attempt_rebias) {
    // 撤銷匿名偏向,當需要計算對象的 identity hash code 時,會走到這裏
    markOop biased_value       = mark;
    markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age());
    markOop res_mark = (markOop) Atomic::cmpxchg_ptr(unbiased_prototype, obj->mark_addr(), mark);
    if (res_mark == biased_value) {
      return BIAS_REVOKED;
    }
  } else if (mark->has_bias_pattern()) {
    Klass* k = obj->klass(); // 對象的類元數據
    markOop prototype_header = k->prototype_header();
    // 類禁用了偏向模式
    if (!prototype_header->has_bias_pattern()) {
      markOop biased_value       = mark;
      markOop res_mark = (markOop) Atomic::cmpxchg_ptr(prototype_header, obj->mark_addr(), mark);
      assert(!(*(obj->mark_addr()))->has_bias_pattern(), "even if we raced, should still be revoked");
      return BIAS_REVOKED;
    // 對象的 epoch 不等於 類的 epoch
    } else if (prototype_header->bias_epoch() != mark->bias_epoch()) {
      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());
        markOop res_mark = (markOop) Atomic::cmpxchg_ptr(rebiased_prototype, obj->mark_addr(), mark);
        if (res_mark == biased_value) {
          return BIAS_REVOKED_AND_REBIASED;
        }
      } else {
        markOop biased_value       = mark;
        markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age());
        markOop res_mark = (markOop) Atomic::cmpxchg_ptr(unbiased_prototype, obj->mark_addr(), mark);
        if (res_mark == biased_value) {
          return BIAS_REVOKED;
        }
      }
    }
  }

  // 下面準備開始偏向撤銷,首先判斷是否需要批量重偏向或批量撤銷
  HeuristicsResult heuristics = update_heuristics(obj(), attempt_rebias);
  // 對象不是偏向模式,直接返回
  if (heuristics == HR_NOT_BIASED) {
    return NOT_BIASED;
  // 單個偏向的撤銷
  } else if (heuristics == HR_SINGLE_REVOKE) {
    Klass *k = obj->klass();
    markOop prototype_header = k->prototype_header();
    // 偏向所有者是自身,且 epoch 沒有過期
    if (mark->biased_locker() == THREAD &&
        prototype_header->bias_epoch() == mark->bias_epoch()) {
      // 這個分支用於處理,有效地偏向所有者要計算對象 identity hash code 的情況
      // 單獨處理是因爲,這種情況在線程自己的棧處理就好了,不需要等到 safe point
      ResourceMark rm;
      if (TraceBiasedLocking) {
        tty->print_cr("Revoking bias by walking my own stack:");
      }
      BiasedLocking::Condition cond = revoke_bias(obj(), false, false, (JavaThread*) THREAD);
      ((JavaThread*) THREAD)->set_cached_monitor_info(NULL);
      assert(cond == BIAS_REVOKED, "why not?");
      return cond;
    } else {
      // 否則,讓 VM 線程在 safepoint 調用 VM_RevokeBias 的 doit 方法撤銷偏向
      VM_RevokeBias revoke(&obj, (JavaThread*) THREAD);
      VMThread::execute(&revoke);
      return revoke.status_code();
    }
  }

  assert((heuristics == HR_BULK_REVOKE) ||
         (heuristics == HR_BULK_REBIAS), "?");
  // 如果走到這裏,說明觸發了批量重偏向或批量撤銷
  // VM 線程在 safepoint 調用 VM_BulkRevokeBias 的 doit 方法
  VM_BulkRevokeBias bulk_revoke(&obj, (JavaThread*) THREAD,
                                (heuristics == HR_BULK_REBIAS),
                                attempt_rebias); // 此時,這個參數爲 true
  VMThread::execute(&bulk_revoke);
  return bulk_revoke.status_code();
}

看一下 VM_RevokeBias 的 doit 方法(可以將 VM 線程的 doit 方法類比於 Java 線程的 run 方法)

  virtual void doit() {
    if (_obj != NULL) {
      if (TraceBiasedLocking) {
        tty->print_cr("Revoking bias with potentially per-thread safepoint:");
      }
      // 鎖對象不爲 NULL,調用 revoke_bias 撤銷偏向
      _status_code = revoke_bias((*_obj)(), false, false, _requesting_thread);
      clean_up_cached_monitor_info();
      return;
    } else {
      if (TraceBiasedLocking) {
        tty->print_cr("Revoking bias with global safepoint:");
      }
      BiasedLocking::revoke_at_safepoint(_objs);
    }
  }

單個對象的偏向撤銷,可以參考:源碼解析-偏向鎖撤銷流程解讀
撤銷結果是:
如果偏向所有者,也就是線程 B,它正在持有該偏向鎖,則將該偏向鎖撤銷爲 B 持有的輕量級鎖。
如果線程 B 沒有正在持有該偏向鎖,則將該偏向鎖撤銷爲無鎖。
這兩種情況都會在隨後的 slow_enter 方法進行鎖升級。(前面 ObjectSynchronizer::fast_enter 方法裏的邏輯)

再看看 VM_BulkRevokeBias 的 doit 方法

  virtual void doit() {
    _status_code = bulk_revoke_or_rebias_at_safepoint((*_obj)(), _bulk_rebias, _attempt_rebias_of_object, _requesting_thread);
    clean_up_cached_monitor_info();
  }

如果發生了批量重偏向或批量撤銷,VM 線程會執行到 BiasedLocking::Condition bulk_revoke_or_rebias_at_safepoint

static BiasedLocking::Condition bulk_revoke_or_rebias_at_safepoint(oop o,
                                                                   bool bulk_rebias,
                                                                   bool attempt_rebias_of_object,
                                                                   JavaThread* requesting_thread) {
  ...
  // 當前時間
  jlong cur_time = os::javaTimeMillis();
  // 將這次重偏向時間寫入類元數據,作爲下次觸發批量重偏向或批量撤銷的啓發條件之一
  o->klass()->set_last_biased_lock_bulk_revocation_time(cur_time);


  Klass* k_o = o->klass(); // 觸發重偏向對象的 類元數據
  Klass* klass = k_o;

  if (bulk_rebias) {
  	// 這個分支是批量重偏向的邏輯
    // 類開啓了偏向模式,才進行批量重偏向
    if (klass->prototype_header()->has_bias_pattern()) {
      int prev_epoch = klass->prototype_header()->bias_epoch();
      // 自增類的 epoch
      klass->set_prototype_header(klass->prototype_header()->incr_bias_epoch());
      // 獲取類自增後的 epoch
      int cur_epoch = klass->prototype_header()->bias_epoch();

      // 遍歷所有線程
      for (JavaThread* thr = Threads::first(); thr != NULL; thr = thr->next()) {
        GrowableArray<MonitorInfo*>* cached_monitor_info = get_or_compute_monitor_info(thr);
        // 遍歷線程所有的鎖記錄
        for (int i = 0; i < cached_monitor_info->length(); i++) {
          MonitorInfo* mon_info = cached_monitor_info->at(i);
          oop owner = mon_info->owner();
          markOop mark = owner->mark();
          // 找到所有當前類的偏向鎖對象
          if ((owner->klass() == k_o) && mark->has_bias_pattern()) {
            // We might have encountered this object already in the case of recursive locking
            assert(mark->bias_epoch() == prev_epoch || mark->bias_epoch() == cur_epoch, "error in bias epoch adjustment");
            // 更新該類偏向鎖對象的 epoch 與 類的 epoch 保持一致
            owner->set_mark(mark->set_bias_epoch(cur_epoch));
          }
        }
      }
    }

    // 撤銷當前偏向鎖
    // 此時,由於參數 attempt_rebias_of_object 爲 true
    // 所以,如果偏向所有者沒有正在持有該偏向鎖,該鎖會撤銷爲匿名偏向鎖
    // 否則,撤銷爲輕量級鎖
    revoke_bias(o, attempt_rebias_of_object && klass->prototype_header()->has_bias_pattern(), true, requesting_thread);
  } else {
    ...

    // 這個分支是批量撤銷的邏輯
    // 首先,禁用 類元數據 裏的可偏向屬性
    // markOopDesc::prototype() 返回的是一個關閉偏向模式的 prototype
    klass->set_prototype_header(markOopDesc::prototype());

    // 其次,遍歷所有線程的棧,撤銷該類正在被持有的偏向鎖爲輕量級鎖
    for (JavaThread* thr = Threads::first(); thr != NULL; thr = thr->next()) {
      GrowableArray<MonitorInfo*>* cached_monitor_info = get_or_compute_monitor_info(thr);
      for (int i = 0; i < cached_monitor_info->length(); i++) {
        MonitorInfo* mon_info = cached_monitor_info->at(i);
        oop owner = mon_info->owner();
        markOop mark = owner->mark();
        if ((owner->klass() == k_o) && mark->has_bias_pattern()) {
          // 具體撤銷,還是通過 revoke_bias() 方法去做
          revoke_bias(owner, false, true, requesting_thread);
        }
      }
    }

    // 當前鎖對象可能未被任何線程持有
    // 所以這裏單獨進行撤銷,以確保完成調用方的撤銷語義
    revoke_bias(o, false, true, requesting_thread);
  }

  ...

  BiasedLocking::Condition status_code = BiasedLocking::BIAS_REVOKED;

  if (attempt_rebias_of_object &&
      o->mark()->has_bias_pattern() &&
      klass->prototype_header()->has_bias_pattern()) {
    // 參數 attempt_rebias_of_object 爲 true
    // 並且發生的是批量重偏向
    // 而且偏向所有者沒有正在持有該偏向鎖,會走到這裏
    // 如果滿足上面三個條件,則說明當前鎖對象已經被撤銷爲了匿名偏向
    // 下面直接讓該鎖重偏向於當前線程
    markOop new_mark = markOopDesc::encode(requesting_thread, o->mark()->age(),
                                           klass->prototype_header()->bias_epoch());
    o->set_mark(new_mark); // 由於是在安全點裏,所以不需要 CAS 操作
    // 注意這裏的返回值,這個返回值傳到 fast_enter 方法後
    // fast_enter 方法就直接返回了,而不會執行到後面的 slow_enter 方法
    status_code = BiasedLocking::BIAS_REVOKED_AND_REBIASED;
    ...
    }
  }

  ...

  return status_code;
}

此時,第一類情況裏的第二種已經加鎖完成。
剩下的所有情況,都要接着走到 slow_enter 裏進行鎖升級。
ObjectSynchronizer::slow_enter 源碼如下:

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()) {
    // 設置鎖記錄的 displaced mark word
    lock->set_displaced_header(mark);
    // 嘗試將鎖記錄的地址寫入對象的 mark word
    if (mark == (markOop) Atomic::cmpxchg_ptr(lock, obj()->mark_addr(), mark)) {
      TEVENT (slow_enter: release stacklock) ;
      return ; // 第一類情況的第一和第三種,在這裏加鎖成功後返回
    }
    // 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;
  }

#if 0
  // The following optimization isn't particularly useful.
  if (mark->has_monitor() && mark->monitor()->is_entered(THREAD)) {
    lock->set_displaced_header (NULL) ;
    return ;
  }
#endif

  // 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.
  lock->set_displaced_header(markOopDesc::unused_mark());
  // 這裏先是調用 inflate 方法將鎖膨脹爲重量級鎖
  // 然後又調用其返回對象的 enter 方法處理重量級鎖的獲取請求
  ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD);
}

此時,第一類情況的第一和第三種,如果在這裏成功加上輕量級鎖,就可以返回了。
其餘情況需要接着走到 ObjectSynchronizer::inflate ,該方法會爲鎖對象分配 ObjectMonitor 對象(就是我們常說的重量級鎖的 monitor),然後 slow_enter 調用 ObjectMonitor 的 enter 方法處理重量級鎖的獲取請求。


ObjectMonitor * ATTR ObjectSynchronizer::inflate (Thread * Self, oop object) {
  // Inflate mutates the heap ...
  // Relaxing assertion for bug 6320749.
  assert (Universe::verify_in_progress() ||
          !SafepointSynchronize::is_at_safepoint(), "invariant") ;

  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

      // 如果已經膨脹成功,則直接返回
      if (mark->has_monitor()) {
          ...
          return inf ;
      }

      // 如果有其它線程先一步正在膨脹當前鎖對象,當前線程只能等待其膨脹完成
      if (mark == markOopDesc::INFLATING()) {
         TEVENT (Inflate: spin while INFLATING) ;
         ReadStableMark(object) ;
         continue ;
      }

      // 輕量級鎖膨脹的情況
      if (mark->has_locker()) {
      	  // 爲鎖對象分配一個 objectmonitor 對象,並初始化其值
          ObjectMonitor * m = omAlloc (Self) ;
          
          m->Recycle();
          m->_Responsible  = NULL ;
          m->OwnerIsThread = 0 ;
          m->_recursions   = 0 ;
          m->_SpinDuration = ObjectMonitor::Knob_SpinLimit ;   // Consider: maintain by type/class
		  // 嘗試在對象 mark word 寫入 INFLATING 標誌
          markOop cmp = (markOop) Atomic::cmpxchg_ptr (markOopDesc::INFLATING(), object->mark_addr(), mark) ;
          // 寫入成功,則獲得膨脹資格,否則其它線程獲得膨脹資格,當前線程只能等待
          if (cmp != mark) {
             omRelease (Self, m, true) ;
             continue ;       // Interference -- just retry
          }

		  // 源碼這裏解釋了爲什麼要有一個 INFLATING 狀態
          // We've successfully installed INFLATING (0) into the mark-word.
          // This is the only case where 0 will appear in a mark-work.
          // Only the singular thread that successfully swings the mark-word
          // to 0 can perform (or more precisely, complete) inflation.
          //
          // Why do we CAS a 0 into the mark-word instead of just CASing the
          // mark-word from the stack-locked value directly to the new inflated state?
          // Consider what happens when a thread unlocks a stack-locked object.
          // It attempts to use CAS to swing the displaced header value from the
          // on-stack basiclock back into the object header.  Recall also that the
          // header value (hashcode, etc) can reside in (a) the object header, or
          // (b) a displaced header associated with the stack-lock, or (c) a displaced
          // header in an objectMonitor.  The inflate() routine must copy the header
          // value from the basiclock on the owner's stack to the objectMonitor, all
          // the while preserving the hashCode stability invariants.  If the owner
          // decides to release the lock while the value is 0, the unlock will fail
          // and control will eventually pass from slow_exit() to inflate.  The owner
          // will then spin, waiting for the 0 value to disappear.   Put another way,
          // the 0 causes the owner to stall if the owner happens to try to
          // drop the lock (restoring the header from the basiclock to the object)
          // while inflation is in-progress.  This protocol avoids races that might
          // would otherwise permit hashCode values to change or "flicker" for an object.
          // Critically, while object->mark is 0 mark->displaced_mark_helper() is stable.
          // 0 serves as a "BUSY" inflate-in-progress indicator.


          
          // 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.
          // 從所有者的棧中獲取 displaced mark
          markOop dmw = mark->displaced_mark_helper() ;
          assert (dmw->is_neutral(), "invariant") ;

          m->set_header(dmw) ;

          // 設置監視器的 owner 爲對象 mark word 指向的鎖記錄
          m->set_owner(mark->locker());
          m->set_object(object);

          guarantee (object->mark() == markOopDesc::INFLATING(), "invariant") ;
          // 設置對象 mark word 爲重量級鎖狀態
          object->release_set_mark(markOopDesc::encode(m));

          ...
          return m ;
      }

      // 無鎖狀態膨脹的情況
      assert (mark->is_neutral(), "invariant");
      ObjectMonitor * m = omAlloc (Self) ;
      // prepare m for installation - set monitor to initial state
      m->Recycle();
      m->set_header(mark);
      m->set_owner(NULL);
      m->set_object(object);
      m->OwnerIsThread = 1 ;
      m->_recursions   = 0 ;
      m->_Responsible  = NULL ;
      m->_SpinDuration = ObjectMonitor::Knob_SpinLimit ;       // consider: keep metastats by type/class

      if (Atomic::cmpxchg_ptr (markOopDesc::encode(m), object->mark_addr(), mark) != mark) {
          // 替換對象頭的 mark word 爲重量級鎖狀態不成功
          // 說明有另外一個線程膨脹成功,釋放 monitor 對象
          m->set_object (NULL) ;
          m->set_owner  (NULL) ;
          m->OwnerIsThread = 0 ;
          m->Recycle() ;
          omRelease (Self, m, true) ;
          m = NULL ;
          continue ;
      }

      ...
      return m ;
  }
}

隨後來到 ObjectMonitor::enter

void ATTR ObjectMonitor::enter(TRAPS) {
  Thread * const Self = THREAD ;
  void * cur ;
  // 重量級鎖的 owner 爲 NULL,表示當前重量級鎖是剛從無鎖狀態膨脹上來的
  // 如果能 CAS 設置成功,則當前線程直接獲得鎖
  cur = Atomic::cmpxchg_ptr (Self, &_owner, NULL) ;
  if (cur == NULL) {
     // Either ASSERT _recursions == 0 or explicitly set _recursions = 0.
     assert (_recursions == 0   , "invariant") ;
     assert (_owner      == Self, "invariant") ;
     // CONSIDER: set or assert OwnerIsThread == 1
     return ;
  }
  
  // 重量級鎖重入
  if (cur == Self) {
     // TODO-FIXME: check for integer overflow!  BUGID 6557169.
     _recursions ++ ;
     return ;
  }
  
  // 如果重量級鎖的 owner 指向當前線程的鎖記錄
  // 則說明當前重量級鎖是剛從當前線程持有的輕量級鎖膨脹上來的
  // 當前線程已經持有了該重量級鎖,做一些設置後即可返回
  if (Self->is_lock_owned ((address)cur)) {
    assert (_recursions == 0, "internal state error");
    // 初始化重入計數
    _recursions = 1 ;
    // 把原本指向線程鎖記錄的 owner,改成指向線程
    _owner = Self ;
    OwnerIsThread = 1 ;
    return ;
  }

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

  // 先嚐試自旋獲取鎖
  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 ;
  }

  ...

  // 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_ptr(&_count);

  EventJavaMonitorEnter event;

  { // Change java thread status to indicate blocked on monitor enter.
  	// 更改 java 線程狀態
    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);
    }

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

    // TODO-FIXME: change the following for(;;) loop to straight-line code.
    for (;;) {
      jt->set_suspend_equivalent();
	  // 在該方法中決定當前線程是獲取鎖還是進行系統調用掛起當前線程
      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_ptr(&_count);
  assert (_count >= 0, "invariant") ;
  Self->_Stalled = 0 ;

  ...

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

  if (event.should_commit()) {
    event.set_klass(((oop)this->object())->klass());
    event.set_previousOwner((TYPE_JAVALANGTHREAD)_previous_owner_tid);
    event.set_address((TYPE_ADDRESS)(uintptr_t)(this->object_addr()));
    event.commit();
  }

  if (ObjectMonitor::_sync_ContendedLockAttempts != NULL) {
     ObjectMonitor::_sync_ContendedLockAttempts->inc() ;
  }
}

最後在 ObjectMonitor::EnterI 方法裏,要麼當前線程獲取到重量級鎖,要麼進行系統調用掛起當前線程。

參考:

  1. openjdk8 的源碼
  2. 死磕Synchronized底層實現 系列
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章