源码解析-线程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底层实现 系列
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章