JDK18:Synchronizer的cpp源码分析(三)——重量级锁释放、wait()、notify()

82 阅读7分钟

感觉这个源码不是很有营养,今天写的会粗糙一些,主要过一遍方法流程

1、wait()——获取重量级锁

int ObjectSynchronizer::wait(Handle obj, jlong millis, TRAPS) {
  //获得当前线程引用
  JavaThread* current = THREAD;
  // 等待时间不能小于0
  if (millis < 0) {
    THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative");
  }
  // 返回重量级锁对象
  // 如果当前是轻量级锁,则膨胀为重量级锁。如果正在膨胀,则等待。如果已经膨胀完毕,则返回重量级锁对象。
  ObjectMonitor* monitor = inflate(current, obj(), inflate_cause_wait);

  DTRACE_MONITOR_WAIT_PROBE(monitor, obj(), current, millis);
  //进入这里
  monitor->wait(millis, true, THREAD); // Not CHECK as we need following code

  int ret_code = dtrace_waited_probe(monitor, obj, THREAD);
  return ret_code;
}

2、wait()——等待队列

void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {
  JavaThread* current = THREAD;

  assert(InitDone, "Unexpectedly not initialized");

  CHECK_OWNER();  // Throws IMSE if not owner.

  EventJavaMonitorWait event;

  // check for a pending interrupt
  // 检查打断
  if (interruptible && current->is_interrupted(true) && !HAS_PENDING_EXCEPTION) {
    if (JvmtiExport::should_post_monitor_waited()) {
      JvmtiExport::post_monitor_waited(current, this, false);
    }
    if (event.should_commit()) {
      post_monitor_wait_event(&event, this, 0, millis, false);
    }
    // 抛出打断异常
    THROW(vmSymbols::java_lang_InterruptedException());
    return;
  }

  assert(current->_Stalled == 0, "invariant");
  current->_Stalled = intptr_t(this);
  current->set_current_waiting_monitor(this);


  //创建等待队列节点
  ObjectWaiter node(current);
  //节点状态设置为TS_WAIT
  node.TState = ObjectWaiter::TS_WAIT;
  current->_ParkEvent->reset();
  //插入万能屏障
  OrderAccess::fence();          // ST into Event; membar ; LD interrupted-flag

  Thread::SpinAcquire(&_WaitSetLock, "WaitSet - add");
  //将节点加入到等待队列
  AddWaiter(&node);
  Thread::SpinRelease(&_WaitSetLock);

  _Responsible = NULL;

  intx save = _recursions;     // record the old recursion count
  //等待队列计数+1
  _waiters++;                  // increment the number of waiters
  _recursions = 0;             // set the recursion level to be 1
  //释放重量级锁
  exit(current);               // exit the monitor
  guarantee(owner_raw() != current, "invariant");

  int ret = OS_OK;
  int WasNotified = 0;

  // 获取打断标志
  bool interrupted = interruptible && current->is_interrupted(false);

  { // State transition wrappers
    OSThread* osthread = current->osthread();
    OSThreadWaitState osts(osthread, true);

    assert(current->thread_state() == _thread_in_vm, "invariant");

    {
      ClearSuccOnSuspend csos(this);
      ThreadBlockInVMPreprocess<ClearSuccOnSuspend> tbivs(current, csos, true /* allow_suspend */);
      //检查打断
      if (interrupted || HAS_PENDING_EXCEPTION) {
        // Intentionally empty
          
        //检查有没有被notify
      } else if (node._notified == 0) {
         //miles = 0 ,无限期park,等待别的线程唤醒
        if (millis <= 0) {
          current->_ParkEvent->park();
        } else {
          // millis > 0 ,park一定时间
          ret = current->_ParkEvent->park(millis);
        }
      }
    }

    //到这里说明被唤醒了
    if (node.TState == ObjectWaiter::TS_WAIT) {
      Thread::SpinAcquire(&_WaitSetLock, "WaitSet - unlink");
      if (node.TState == ObjectWaiter::TS_WAIT) {
        //从等待队列中删除节点
        DequeueSpecificWaiter(&node);       // unlink from WaitSet
        assert(node._notified == 0, "invariant");

        //重置状态为TS_RUN
        node.TState = ObjectWaiter::TS_RUN;
      }
      Thread::SpinRelease(&_WaitSetLock);
    }

    guarantee(node.TState != ObjectWaiter::TS_WAIT, "invariant");
    OrderAccess::loadload();
    if (_succ == current) _succ = NULL;
    WasNotified = node._notified;

    if (JvmtiExport::should_post_monitor_waited()) {
      JvmtiExport::post_monitor_waited(current, this, ret == OS_TIMEOUT);
      if (node._notified != 0 && _succ == current) {
        node._event->unpark();
      }
    }

    if (event.should_commit()) {
      post_monitor_wait_event(&event, this, node._notifier_tid, millis, ret == OS_TIMEOUT);
    }

    OrderAccess::fence();

    assert(current->_Stalled != 0, "invariant");
    current->_Stalled = 0;

    assert(owner_raw() != current, "invariant");
    ObjectWaiter::TStates v = node.TState;

    // 进入重新获取锁的逻辑
    // 判断状态,确保这期间状态仍然是RUN
    if (v == ObjectWaiter::TS_RUN) {
        // 重量级锁获取,这个之前文章说过了
      enter(current);
    } else {
      guarantee(v == ObjectWaiter::TS_ENTER || v == ObjectWaiter::TS_CXQ, "invariant");
      // 这个方法简单说就是看看是不是Enter或者CXQ状态,如果是的话尝试自旋获取锁,获取不到就继续park
      ReenterI(current, &node);
      node.wait_reenter_end(this);
    }
    
    ...下面的不是很重要 省略了
}
  • 简单总结一下wait()流程:
  • wait()--->获取重量级锁(当前是轻量级锁则膨胀)--->线程加入等待队列--->释放重量级锁--->线程被挂起--->被唤醒后重新进入获取重量级锁流程

3、exit() 释放锁&EntryList

void ObjectMonitor::exit(JavaThread* current, bool not_suspended) {
  // 获取持锁线程
  void* cur = owner_raw();
  if (current != cur) {
    if (current->is_lock_owned((address)cur)) {
      // 如果当前线程就是持锁线程
      assert(_recursions == 0, "invariant");
      //关联轻量级锁持有者
      set_owner_from_BasicLock(cur, current);  // Convert from BasicLock* to Thread*.
      _recursions = 0;
    } else {
    //如果当前线程不是持锁线程
#ifdef ASSERT
      LogStreamHandle(Error, monitorinflation) lsh;
      lsh.print_cr("ERROR: ObjectMonitor::exit(): thread=" INTPTR_FORMAT
                    " is exiting an ObjectMonitor it does not own.", p2i(current));
      lsh.print_cr("The imbalance is possibly caused by JNI locking.");
      print_debug_style_on(&lsh);
      assert(false, "Non-balanced monitor enter/exit!");
#endif

      //直接return,非持锁线程不能释放
      return;
    }
  }

  if (_recursions != 0) {
    _recursions--;        // this is simple recursive enter
    return;
  }
  _Responsible = NULL;

#if INCLUDE_JFR
  if (not_suspended && EventJavaMonitorEnter::is_enabled()) {
    _previous_owner_tid = JFR_THREAD_ID(current);
  }
#endif

  for (;;) {
    assert(current == owner_raw(), "invariant");

    //释放锁并清除锁的持有者
    release_clear_owner(current);
    OrderAccess::storeload();

    // 先检查EntryList和cxq是否为空,如果俩者都为空直接return,没有要排队获取锁的线程,什么都不需要做
    if ((intptr_t(_EntryList)|intptr_t(_cxq)) == 0 || _succ != NULL) {
      return;
    }

    // 如果这个方法结果不为NULL,说明在这期间重量级锁被别的线程抢占获取了,这时也可以return了
    if (try_set_owner_from(NULL, current) != NULL) {
      return;
    }

    guarantee(owner_raw() == current, "invariant");

    ObjectWaiter* w = NULL;

    // 获取EntryList
    w = _EntryList;
    if (w != NULL) {
      assert(w->TState == ObjectWaiter::TS_ENTER, "invariant");
      // 唤醒EntryList中头节点
      ExitEpilog(current, w);
      return;
    }

    //下面这块代码的作用
    //将 _cxq 中的节点转移到 _EntryList 中,然后将 _EntryList 中的节点转换成一个双向链表。
    //这样做的目的是将原来在 _cxq 中排队的线程移到 _EntryList 中
    //将它们的状态从等待状态(TS_CXQ)改为进入状态(TS_ENTER)。
    w = _cxq;
    if (w == NULL) continue;

    for (;;) {
      assert(w != NULL, "Invariant");
      ObjectWaiter* u = Atomic::cmpxchg(&_cxq, w, (ObjectWaiter*)NULL);
      if (u == w) break;
      w = u;
    }

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

    _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;
    }
    
    if (_succ != NULL) continue;

    w = _EntryList;
    if (w != NULL) {
      guarantee(w->TState == ObjectWaiter::TS_ENTER, "invariant");
      //唤醒EntryList中头的节点
      ExitEpilog(current, w);
      return;
    }
  }
}
  • 简单总结一些流程:执行exit()--->判断线程是否是持锁线程--->进入循环---->释放重量级锁--->进入几种不同模式:
  • 1.cxq和entryList都没竞争线程,直接return,不需要后续操作了。
  • 2.尝试获取重量级锁时发现已被其他线程抢占,直接return。
  • 3.将cxq中的节点转移到EntryList,形成一个双向链表,然后每次从EntryList中取头节点unpark(唤醒)

4、notify()——唤醒等待队列中的线程

void ObjectSynchronizer::notify(Handle obj, TRAPS) {
  //获取当前线程
  JavaThread* current = THREAD;
  //获取加锁对象的markword
  markWord mark = obj->mark();
  //如果没有膨胀过,说明无竞争,不需要唤醒,直接return
  if (mark.has_locker() && current->is_lock_owned((address)mark.locker())) {
    // Not inflated so there can't be any waiters to notify.
    return;
  }
  // 获取重量级锁
  ObjectMonitor* monitor = inflate(current, obj(), inflate_cause_notify);
  // 进入唤醒流程
  monitor->notify(CHECK);
}
void ObjectMonitor::notify(TRAPS) {
  JavaThread* current = THREAD;
  //检查当前线程是否是锁持有者
  CHECK_OWNER();  // Throws IMSE if not owner.
  //如果等待队列为空,无需唤醒,直接return
  if (_WaitSet == NULL) {
    return;
  }
  DTRACE_MONITOR_PROBE(notify, this, object(), current);

  //进这里
  INotify(current);
  OM_PERFDATA_OP(Notifications, inc(1));
}
void ObjectMonitor::INotify(JavaThread* current) {
  Thread::SpinAcquire(&_WaitSetLock, "WaitSet - notify");

  //这里面取出了等待队列的头节点,并且从队列中移除了头节点
  ObjectWaiter* iterator = DequeueWaiter();
  //如果头节点不为空
  if (iterator != NULL) {
    guarantee(iterator->TState == ObjectWaiter::TS_WAIT, "invariant");
    guarantee(iterator->_notified == 0, "invariant");

    //修改节点状态为ENTER
    iterator->TState = ObjectWaiter::TS_ENTER;

    //修改其状态为已被notify
    iterator->_notified = 1;
    iterator->_notifier_tid = JFR_THREAD_ID(current);

    //获取EntryList,准备把waitset中的节点放进EntryList
    ObjectWaiter* list = _EntryList;
    if (list != NULL) {
      assert(list->_prev == NULL, "invariant");
      assert(list->TState == ObjectWaiter::TS_ENTER, "invariant");
      assert(list != iterator, "invariant");
    }

    // prepend to cxq
    if (list == NULL) {
      //如果此时EntryList为空,只取出一个节点放入EntryList
      //节点前后指针置为空,不能放入其他节点
      iterator->_next = iterator->_prev = NULL;
      //将节点放入EntryList
      _EntryList = iterator;
    } else {
      // 修改节点状态为CXQ
      iterator->TState = ObjectWaiter::TS_CXQ;
      for (;;) {
        //将waitSet中一个节点放入_cxq
        ObjectWaiter* front = _cxq;
        iterator->_next = front;
        if (Atomic::cmpxchg(&_cxq, front, iterator) == front) {
          break;
        }
      }

    iterator->wait_reenter_begin(this);
  }
  Thread::SpinRelease(&_WaitSetLock);
}

  • 简单总结一下流程:
  • 1.如果锁是轻量级锁,说明无竞争直接返回
  • 2.获取重量级锁
  • 3.waitSet如果为空直接返回
  • 4.waitSet不为空--->取出waitSet头节点放入EntryList,状态改为ENTER,作为waitSet中第一竞争优先者。
  • 5.剩余节点放入cxq,继续等着吧,等待下次锁被释放被转移到EntryList它才能竞争锁。

5、一点总结

EntryList填充时机:notify时EntryList为空情况下waitSet的第一个节点;Exit时cxq中的节点转移

EntryList出队时机:Exit时竞争锁

cxq填充时机:重量级锁竞争,自旋无数次失败后存入;notify时waitSet的第一个节点存入

cxq出队时机:Exit时向EntryList中转移