Jdk源码阅读 :synchronized 底层实现

关键字 synchronized

synchronized,每个java程序员都用过的关键字。它可以拿来修饰方法、属性、代码块,它是一把锁,用来线程同步 balala 。。。我们对于synchronized的基础知识大概就这么多。然而作为java并发编程的基础,java面试重灾区,我们需要更深入的来了解它。它的底层实现原理,特点等等。
提醒:本文有大量的C++代码,读起来会很痛苦,不过没关系,尝试坚持一下,多少会有些收获。

monitorenter指令
public class SychronizeTest {

    private static final Object lock = new Object();

    public static void main(String[] args) {
        synchronized (lock){
            int i=1;
        }
    }
}

我们使用javap -c SychornizeTest.class拿到上面的类的字节码如下,可以看到第5行第9行有monitorenter 和monitorexit两行指令。我们知道这两个指令便是synchronized实现同步的关键。接下来我们一层层去openJdk源码中去找加锁过程的具体实现。

public class com.fc.study.SychoronizeTest {
  public com.fc.study.SychoronizeTest();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #2                  // Field lock:Ljava/lang/Object;
       3: dup
       4: astore_1
       5: monitorenter
       6: iconst_1
       7: istore_2
       8: aload_1
       9: monitorexit
      10: goto          18
      13: astore_3
      14: aload_1
      15: monitorexit
      16: aload_3
      17: athrow
      18: return
    Exception table:
       from    to  target type
           6    10    13   any
          13    16    13   any

  static {};
    Code:
       0: new           #3                  // class java/lang/Object
       3: dup
       4: invokespecial #1                  // Method java/lang/Object."<init>":()V
       7: putstatic     #2                  // Field lock:Ljava/lang/Object;
      10: return
}
ObjectSynchronizer::enter

通过查找,我们发现在hotspot实现中有这么一个类,ObjectSynchronizer。在这个类中获取到了对象头markword,并且尝试获取锁。如果不成功则会生成一个monitor调用它的enter方法。

void ObjectSynchronizer::enter(Handle obj, BasicLock* lock, TRAPS) {
  if (obj->klass()->is_value_based()) {
    handle_sync_on_value_based_class(obj, THREAD);
  }

  if (UseBiasedLocking) {
    if (!SafepointSynchronize::is_at_safepoint()) {
      BiasedLocking::revoke(obj, THREAD);
    } else {
      BiasedLocking::revoke_at_safepoint(obj);
    }
  }

  // 获取对象头的markword
  markWord mark = obj->mark();
  assert(!mark.has_bias_pattern(), "should not see bias pattern here");

  // 如果 mark word 是无锁态的,直接尝试获取锁
  if (mark.is_neutral()) {
    lock->set_displaced_header(mark);
    if (mark == obj()->cas_set_mark(markWord::from_pointer(lock), mark)) {
      return;
    }
  } 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().value(), "don't relock with same BasicLock");
    lock->set_displaced_header(markWord::from_pointer(NULL));
    return;
  }

  lock->set_displaced_header(markWord::unused_mark());

  while (true) {
    ObjectMonitor* monitor = inflate(THREAD, obj(), inflate_cause_monitor_enter);
    if (monitor->enter(THREAD)) {
      return;
    }
  }
}
ObjcetMonitor::enter

我们继续往下跟.
下面是ObjectMonitor主要的成员变量,我们可以看到它有markword,当前占有monitor的线程id,重入计数等很多变量,这些变量在后面的加锁过程都会用到。除此之外,我们可以看到openJdk做了一些很精巧的设计,它在_header变量和_owner变量中间加了一些东西将两个变量分割在不同的缓存行里,这样可以提高两个变量并发访问的效率(关于缓存行有兴趣的读者可以去查一下资料)。我们继续。

  JVMCI_ONLY(friend class JVMCIVMStructs;)

  static OopStorage* _oop_storage;

  // The sync code expects the header field to be at offset zero (0).
  // Enforced by the assert() in header_addr().
  volatile markWord _header;        // 对象头的markword displaced object header word - mark
  WeakHandle _object;               // backward object pointer
  // 2.将header和owner分割在不同的缓存行里--为了提升读写效率
  // Separate _header and _owner on different cache lines since both can
  // have busy multi-threaded access. _header and _object are set at initial
  // inflation. The _object does not change, so it is a good choice to share
  // its cache line with _header.
  DEFINE_PAD_MINUS_SIZE(0, OM_CACHE_LINE_SIZE, sizeof(volatile markWord) +
                        sizeof(WeakHandle));
  // Used by async deflation as a marker in the _owner field:
  #define DEFLATER_MARKER reinterpret_cast<void*>(-1)
  void* _owner;                     // 3.拥有对象monitor的线程 pointer to owning thread OR BasicLock
  volatile jlong _previous_owner_tid;  // 上一个拥有monitor的线程的id thread id of the previous owner of 
  DEFINE_PAD_MINUS_SIZE(1, OM_CACHE_LINE_SIZE, sizeof(void*) +
                        sizeof(volatile jlong));
  ObjectMonitor* _next_om;          // Next ObjectMonitor* linkage
  volatile intx _recursions;        // 一个计数,标记monitor 进入的次数,初始为0recursion 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.

  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;

  jint  _contentions;               // Number of active contentions in enter(). It is used by is_busy()
                                    // along with other fields to determine if an ObjectMonitor can be
                                    // deflated. It is also used by the async deflation protocol. See
                                    // ObjectMonitor::deflate_monitor().
 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
加锁源码

加锁的主方法是下面这个enter方法,我删掉了多余的英文注释,并用中文做了简单注释。我们看到monitor会首先取到自己的线程,然后去尝试将_owner设置成当前线程,用CAS的方式。如果当前_owner是NULL,表示此时没有线程拥有monitor,则直接返回成功。如果_owner是当前线程,则重入计数加一,返回成功。否则往下执行EnterI方法,进行入队阻塞。

bool ObjectMonitor::enter(TRAPS) {
 Thread * const Self = THREAD;

 // 步骤1
 // try_set_owner_from
 // 就是用cas的方法去设置 _owner 的值,它的返回值是 _owner的prev值
 // 如果前值是NULL 则表示第一次加锁成功
 void* cur = try_set_owner_from(NULL, Self);
 if (cur == NULL) {
   assert(_recursions == 0, "invariant");
   return true;
 }

 // 如果_owner前值是当前线程,则表示是当前线程重入这个monitor,重入计数+1
 if (cur == Self) {
   // TODO-FIXME: check for integer overflow!  BUGID 6557169.
   _recursions++;
   return true;
 }
 if (Self->is_lock_owned((address)cur)) {
   assert(_recursions == 0, "internal state error");
   _recursions = 1;
   set_owner_from_BasicLock(cur, Self);  // Convert from BasicLock* to Thread*.
   return true;
 }
 // We've encountered genuine contention.
 assert(Self->_Stalled == 0, "invariant");
 Self->_Stalled = intptr_t(this);

 // 步骤二
 // 在将自己入队之前尝试做一次自旋获取锁,如果自选成功,则当前线程占有monitor
 if (TrySpin(Self) > 0) {
   assert(owner_raw() == Self, "must be Self: owner=" INTPTR_FORMAT, p2i(owner_raw()));
   assert(_recursions == 0, "must be 0: recursions=" INTX_FORMAT, _recursions);
   assert(object()->mark() == markWord::encode(this),
          "object mark must match encoded this: mark=" INTPTR_FORMAT
          ", encoded this=" INTPTR_FORMAT, object()->mark().value(),
          markWord::encode(this).value());
   Self->_Stalled = 0;
   return true;
 }

 assert(owner_raw() != Self, "invariant");
 assert(_succ != Self, "invariant");
 JavaThread * jt = Self->as_Java_thread();
 assert(!SafepointSynchronize::is_at_safepoint(), "invariant");
 assert(jt->thread_state() != _thread_blocked, "invariant");

 // Keep track of contention for JVM/TI and M&M queries.
 add_to_contentions(1);
 if (is_being_async_deflated()) {
   // Async deflation is in progress and our contentions increment
   // above lost the race to async deflation. Undo the work and
   // force the caller to retry.
   const oop l_object = object();
   if (l_object != NULL) {
     // Attempt to restore the header/dmw to the object's header so that
     // we only retry once if the deflater thread happens to be slow.
     install_displaced_markword_in_object(l_object);
   }
   Self->_Stalled = 0;
   add_to_contentions(-1);
   return false;
 }

 JFR_ONLY(JfrConditionalFlushWithStacktrace<EventJavaMonitorEnter> flush(jt);)
 EventJavaMonitorEnter event;
 if (event.is_started()) {
   event.set_monitorClass(object()->klass());
   // Set an address that is 'unique enough', such that events close in
   // time and with the same address are likely (but not guaranteed) to
   // belong to the same object.
   event.set_address((uintptr_t)this);
 }

 { // 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);
   }

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

   // TODO-FIXME: change the following for(;;) loop to straight-line code.
   for (;;) {
     jt->set_suspend_equivalent();
     // 进入EnterI方法,这个方法里包含了入队阻塞的操作
     EnterI(THREAD);
     if (!ExitSuspendEquivalent(jt)) break;
     _recursions = 0;
     _succ = NULL;
     exit(false, Self);

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

 add_to_contentions(-1);
 assert(contentions() >= 0, "must not be negative: contentions=%d", contentions());
 Self->_Stalled = 0;

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

 }
 OM_PERFDATA_OP(ContendedLockAttempts, inc());

 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_previousOwner((uintptr_t)_previous_owner_tid);
   event.commit();
 return true;
}

下面是 EnterI 方法,这个方法包含了入队阻塞的逻辑。可以看到在入队的循环中,monitor主要执行两个逻辑判断,TryLock失败则入队,入队后阻塞。被唤醒之后不断重试。


void ObjectMonitor::EnterI(TRAPS) {
  Thread * const Self = THREAD;
  assert(Self->as_Java_thread()->thread_state() == _thread_blocked, "invariant");

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

  if (try_set_owner_from(DEFLATER_MARKER, Self) == DEFLATER_MARKER) {
    add_to_contentions(1);
    assert(_succ != Self, "invariant");
    assert(_Responsible != Self, "invariant");
    return;
  }

  assert(InitDone, "Unexpectedly not initialized");

  // 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_raw() == 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_raw() != Self, "invariant");
  assert(_Responsible != Self, "invariant");

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

  // 步骤三
  // 将当前的monitor 入队到_cxq,如果入队成功则跳出循环进行下一步,否则尝试获取锁(失败则继续循环,成功则返回)
  ObjectWaiter * nxt;
  for (;;) {
    node._next = nxt = _cxq;
    if (Atomic::cmpxchg(&_cxq, nxt, &node) == 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_raw() == Self, "invariant");
      assert(_Responsible != Self, "invariant");
      return;
    }
  }

  if (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(&_Responsible, Self);
  }
  int nWakeups = 0;
  int recheckInterval = 1;

  // 步骤四:
  // 当前已经入队成功
  // 尝试获取锁,成功则跳出循环往下执行,表示获取到锁
  // 不成功 则parkself 阻塞
  for (;;) {

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

    // park self
    if (_Responsible == Self) {
      Self->_ParkEvent->park((jlong) recheckInterval);
      // Increase the recheckInterval, but clamp the value.
      recheckInterval *= 8;
      if (recheckInterval > MAX_RECHECK_INTERVAL) {
        recheckInterval = MAX_RECHECK_INTERVAL;
      }
    } else {
      Self->_ParkEvent->park();
    }

    if (TryLock(Self) > 0) break;

    if (try_set_owner_from(DEFLATER_MARKER, Self) == DEFLATER_MARKER) {
      add_to_contentions(1);
      break;
    }
    OM_PERFDATA_OP(FutileWakeups, inc());
    ++nWakeups;

    if (TrySpin(Self) > 0) break;
    if (_succ == Self) _succ = NULL;

    OrderAccess::fence();
  }

  assert(owner_raw() == Self, "invariant");

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

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

下面是 TryLock 实现。逻辑很简单,就是尝试去设置_owner为当前线程,设置成功则返回1。

int ObjectMonitor::TryLock(Thread * Self) {
  void* own = owner_raw();
  if (own != NULL) return 0;
  if (try_set_owner_from(NULL, Self) == NULL) {
    assert(_recursions == 0, "invariant");
    return 1;
  }
  return -1;
}

总结

以上,我们可以得出如下结论。
synchronized 关键字由 monitorenter|exit两个指令支持实现的
在jdk层面,enter方法主要由 ObjectSychronizer::enter 和 ObjectMonitor::enter 实现。
ObjectSychronizer 中,jvm获取对象的markword判断,如果无锁态的话则直接尝试获取锁,不会进入monitor的状态。
ObjectMonitor 对象中,标记了当前持有线程_owner,阻塞队列_cxq,重入次数_recursions等。在加锁过程中多次进行TryLock(直接设置当前线程为_owner),多次失败之后最终会将当前线程入队,并阻塞。被唤醒后在此尝试获取锁,成功则退出,失败则继续尝试。

以上便是 jdk 源码层面的 synchoronized 解读。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值