Реализация нижнего уровня Deadly Synchronized — усиленная блокировка

JVM

Эта статья — третья статья о реализации Synchronized на нижнем уровне, а ее содержание — реализация тяжеловесных блокировок.

В этой серии статей мы рассмотрим HotSpotsynchronizedВсесторонний анализ реализации блокировки, включая анализ принципа и исходного кода предвзятых блокировок, облегченных блокировок и тяжелых блокировок, блокировок, разблокировок и процессов обновления блокировок.synchronizedНекоторая помощь от однокурсников на дороге. В основном включают следующие статьи:

Реализация Deadly Synchronized Bottom — Введение

Реализация нижнего уровня Deadly Synchronized — предвзятая блокировка

Базовая реализация Deadly Synchronized — облегченная блокировка

Реализация нижнего уровня Deadly Synchronized — усиленная блокировка

Больше статей смотрите в личном блоге:GitHub.com/farmer Джон Брат…

Тяжелые процессы надувания и блокировки

Когда несколько потоков конкурируют за блокировки одновременно, они входят вsynchronizer.cpp#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()) {
    lock->set_displaced_header(mark);
    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;
  }

 ...
 

  // 这时候需要膨胀为重量级锁,膨胀前,设置Displaced Mark Word为一个特殊值,代表该锁正在用一个重量级锁的monitor
  lock->set_displaced_header(markOopDesc::unused_mark());
  //先调用inflate膨胀为重量级锁,该方法返回一个ObjectMonitor对象,然后调用其enter方法
  ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD);
}

существуетinflateзавершить процесс расширения.

ObjectMonitor * ATTR ObjectSynchronizer::inflate (Thread * Self, oop object) {
  ...

  for (;;) {
      const markOop mark = object->mark() ;
      assert (!mark->has_bias_pattern(), "invariant") ;
    
      // mark是以下状态中的一种:
      // *  Inflated(重量级锁状态)     - 直接返回
      // *  Stack-locked(轻量级锁状态) - 膨胀
      // *  INFLATING(膨胀中)    - 忙等待直到膨胀完成
      // *  Neutral(无锁状态)      - 膨胀
      // *  BIASED(偏向锁)       - 非法状态,在这里不会出现

      // CASE: inflated
      if (mark->has_monitor()) {
          // 已经是重量级锁状态了,直接返回
          ObjectMonitor * inf = mark->monitor() ;
          ...
          return inf ;
      }

      // CASE: inflation in progress
      if (mark == markOopDesc::INFLATING()) {
         // 正在膨胀中,说明另一个线程正在进行锁膨胀,continue重试
         TEVENT (Inflate: spin while INFLATING) ;
         // 在该方法中会进行spin/yield/park等操作完成自旋动作 
         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 (0)状态 
          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() ;
          assert (dmw->is_neutral(), "invariant") ;

          // 设置monitor的字段
          m->set_header(dmw) ;
          // owner为Lock Record
          m->set_owner(mark->locker());
          m->set_object(object);
          ...
          // 将锁对象头设置为重量级锁状态
          object->release_set_mark(markOopDesc::encode(m));

         ...
          return m ;
      }

      // CASE: neutral
  	 
      // 分配以及初始化ObjectMonitor对象
      ObjectMonitor * m = omAlloc (Self) ;
      // prepare m for installation - set monitor to initial state
      m->Recycle();
      m->set_header(mark);
      // owner为NULL
      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
	  // 用CAS替换对象头的mark word为重量级锁状态
      if (Atomic::cmpxchg_ptr (markOopDesc::encode(m), object->mark_addr(), mark) != mark) {
          // 不成功说明有另外一个线程在执行inflate,释放monitor对象
          m->set_object (NULL) ;
          m->set_owner  (NULL) ;
          m->OwnerIsThread = 0 ;
          m->Recycle() ;
          omRelease (Self, m, true) ;
          m = NULL ;
          continue ;
          // interference - the markword changed - just retry.
          // The state-transitions are one-way, so there's no chance of
          // live-lock -- "Inflated" is an absorbing state.
      }

      ...
      return m ;
  }
}

inflateЭто цикл for, предназначенный, в основном, для ситуации, когда несколько потоков вызывают одновременное надувание. Затем он будет обрабатываться по-разному в зависимости от состояния объекта блокировки:

1. Он уже находится в тяжелом состоянии, что указывает на то, что расширение было завершено, и вернуться напрямую

2. Если это легкий замок, требуется операция расширения

3. Если он в расширяющемся состоянии, то занят ожиданием

4. Если это состояние без блокировки, требуется операция расширения.

Облегченные состояния блокировки и блокировки требуют операций расширения.Процесс расширения облегченной блокировки выглядит следующим образом:

1. звонокomAllocназначить одинObjectMonitorОбъект (далее – монитор), вomAllocМетод начнется с приватного потокаmonitorсобиратьomFreeListВыделять объекты в, еслиomFreeListбольше не вmonitorобъект из глобального JVMgFreeListвыделить партиюmonitorприбытьomFreeListсередина.

2. Инициализацияmonitorобъект

3. Установите состояние надувания (НАДУВАНИЕ) состояние

4. НастройкиmonitorПоле заголовкаdisplaced mark word, поле владельцаLock Record, поле obj является объектом блокировки

5. Установите заголовок объекта блокировкиmark wordДля тяжеловесного состояния блокировки, указывая на первый выделенный шагmonitorобъект

Процесс расширения в незаблокированном состоянии выглядит следующим образом:

1. звонокomAllocназначить одинObjectMonitorОбъект (далее монитор)

2. Инициализацияmonitorобъект

3. НастройкиmonitorПоле заголовкаmark word, поле владельцаnull, поле obj является объектом блокировки

4. Установите заголовок объекта блокировкиmark wordДля тяжеловесного состояния блокировки, указывая на первый выделенный шагmonitorобъект

Что касается того, почему облегченным замкам требуется состояние INFLATING, комментарии в коде следующие:

// 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.

Я не совсем понимаю, кто знает, может подсказать~

После завершения инфляции он вызовет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) {
     ...
     return ;
  }
  // 如果是重入的情况
  if (cur == Self) {
     // TODO-FIXME: check for integer overflow!  BUGID 6557169.
     _recursions ++ ;
     return ;
  }
  // 当前线程是之前持有轻量级锁的线程。由轻量级锁膨胀且第一次调用enter方法,那cur是指向Lock Record的指针
  if (Self->is_lock_owned ((address)cur)) {
    assert (_recursions == 0, "internal state error");
    // 重入计数重置为1
    _recursions = 1 ;
    // 设置owner字段为当前线程(之前owner是指向Lock Record的指针)
    _owner = Self ;
    OwnerIsThread = 1 ;
    return ;
  }

  ...

  // 在调用系统的同步操作之前,先尝试自旋获得锁
  if (Knob_SpinEarly && TrySpin (Self) > 0) {
     ...
     //自旋的过程中获得了锁,则直接返回
     Self->_Stalled = 0 ;
     return ;
  }

  ...

  { 
    ...

    for (;;) {
      jt->set_suspend_equivalent();
      // 在该方法中调用系统同步操作
      EnterI (THREAD) ;
      ...
    }
    Self->set_current_pending_monitor(NULL);
    
  }

  ...

}

  1. Если в настоящее время он находится в состоянии без блокировки, блокировка является повторно используемой, а текущий поток является потоком, который ранее удерживал облегченную блокировку, выполняет простую операцию и возвращается.
  2. Первое вращение, чтобы попытаться получить блокировку, цель этого состоит в том, чтобы уменьшить накладные расходы на выполнение операций синхронизации операционной системы.
  3. передачаEnterIметод получает блокировку или блоки

EnterIМетод относительно длинный, прежде чем мы его рассмотрим, поясним общий принцип:

ОдинObjectMonitorОбъект включает в себя несколько ключевых полей: cxq (ContentionList на рисунке ниже), EntryList, WaitSet, owner.

Среди них cxq, EntryList и WaitSet — все структуры связанных списков ObjectWaiter, и владелец указывает на поток, удерживающий блокировку.

1517900250327

Когда поток пытается получить блокировку, если блокировка уже занята, поток инкапсулируется вObjectWaiterОбъект вставляется в голову очереди cxq, после чего вызовparkФункция приостанавливает текущий поток. В системе LinuxparkНижний уровень функции вызывает библиотеку gclib.pthread_cond_wait, ЖДКReentrantLockНижний слой также использует этот метод для подвешивания нити. Подробнее читайте в двух моих предыдущих статьях:Небольшая мысль о синхронизации - далее,Механизм синхронизации на уровне ядра Linux --futex

Когда поток освобождает блокировку, он выбирает поток из cxq или EntryList для пробуждения.Heir presumptiveТо есть предполагаемый наследник (следует переводить так), это тот, что на рисункеReady Thread, предполагая, что наследник попытается получить замок, когда проснется, ноsynchronizedнесправедливо, поэтому предполагаемый наследник не обязательно может получить замок (поэтому он называется «предполагаемым» наследником).

Вызывается, если поток получает блокировкуObject#waitметод, поток будет добавлен в WaitSet, когдаObject#notifyПосле пробуждения поток будет перемещен из WaitSet в cxq или EntryList. Обратите внимание, что при вызове объекта блокировкиwaitилиnotifyметод,Если текущим статусом блокировки является предвзятая блокировка или упрощенная блокировка, она сначала преобразуется в блокировку тяжелого веса..

synchronizedизmonitorМеханизм блокировки и JDKReentrantLockа такжеConditionочень похожа,ReentrantLockСуществует также связанный список потоков, ожидающих получения блокировки,ConditionЕсть и похожийWaitSetКоллекция используется для хранения вызоваawaitразгром. если бы ты был правReentrantLockиметь глубокое понимание, а затем понятьmonitorДолжно быть очень просто.

Вернитесь к коду и начните анализEnterIметод:

void ATTR ObjectMonitor::EnterI (TRAPS) {
    Thread * Self = THREAD ;
    ...
    // 尝试获得锁
    if (TryLock (Self) > 0) {
        ...
        return ;
    }

    DeferredInitialize () ;
 
	// 自旋
    if (TrySpin (Self) > 0) {
        ...
        return ;
    }
    
    ...
	
    // 将线程封装成node节点中
    ObjectWaiter node(Self) ;
    Self->_ParkEvent->reset() ;
    node._prev   = (ObjectWaiter *) 0xBAD ;
    node.TState  = ObjectWaiter::TS_CXQ ;

    // 将node节点插入到_cxq队列的头部,cxq是一个单向链表
    ObjectWaiter * nxt ;
    for (;;) {
        node._next = nxt = _cxq ;
        if (Atomic::cmpxchg_ptr (&node, &_cxq, nxt) == nxt) break ;

        // CAS失败的话 再尝试获得锁,这样可以降低插入到_cxq队列的频率
        if (TryLock (Self) > 0) {
            ...
            return ;
        }
    }

	// SyncFlags默认为0,如果没有其他等待的线程,则将_Responsible设置为自己
    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)) {
            // 当前线程是_Responsible时,调用的是带时间参数的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 {
            //否则直接调用park挂起当前线程
            TEVENT (Inflated enter - park UNTIMED) ;
            Self->_ParkEvent->park() ;
        }

        if (TryLock(Self) > 0) break ;

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

       	...
        // 在释放锁时,_succ会被设置为EntryList或_cxq中的一个线程
        if (_succ == Self) _succ = NULL ;

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

   // 走到这里说明已经获得锁了

    assert (_owner == Self      , "invariant") ;
    assert (object() != NULL    , "invariant") ;
  
	// 将当前线程的node从cxq或EntryList中移除
    UnlinkAfterAcquire (Self, &node) ;
    if (_succ == Self) _succ = NULL ;
	if (_Responsible == Self) {
        _Responsible = NULL ;
        OrderAccess::fence();
    }
    ...
    return ;
}

Есть 3 основных шага:

  1. Вставить текущий поток в начало очереди cxq
  2. Затем припаркуйте текущий поток
  3. Попытка получить замок после пробуждения

Здесь следует отметить, что_Responsibleа также_succРоль двух полей:

Когда происходит гонка, выберите нить в качестве_Responsible,_ResponsibleВызовы тем ограничены по времениparkметод, целью которого является предотвращение возникновения搁浅Феномен.

_succПоток устанавливается, когда поток освобождает блокировку, что означаетHeir presumptive, который является предполагаемым наследником, о котором мы упоминали выше.

разблокировка тяжелого замка

Код для снятия тяжелого замка находится вObjectMonitor::exit:

void ATTR ObjectMonitor::exit(bool not_suspended, TRAPS) {
   Thread * Self = THREAD ;
   // 如果_owner不是当前线程
   if (THREAD != _owner) {
     // 当前线程是之前持有轻量级锁的线程。由轻量级锁膨胀后还没调用过enter方法,_owner会是指向Lock Record的指针。
     if (THREAD->is_lock_owned((address) _owner)) {
       assert (_recursions == 0, "invariant") ;
       _owner = THREAD ;
       _recursions = 0 ;
       OwnerIsThread = 1 ;
     } else {
       // 异常情况:当前不是持有锁的线程
       TEVENT (Exit - Throw IMSX) ;
       assert(false, "Non-balanced monitor enter/exit!");
       if (false) {
          THROW(vmSymbols::java_lang_IllegalMonitorStateException());
       }
       return;
     }
   }
   // 重入计数器还不为0,则计数器-1后返回
   if (_recursions != 0) {
     _recursions--;        // this is simple recursive enter
     TEVENT (Inflated exit - recursive) ;
     return ;
   }

   // _Responsible设置为null
   if ((SyncFlags & 4) == 0) {
      _Responsible = NULL ;
   }

   ...

   for (;;) {
      assert (THREAD == _owner, "invariant") ;

      // Knob_ExitPolicy默认为0
      if (Knob_ExitPolicy == 0) {
         // code 1:先释放锁,这时如果有其他线程进入同步块则能获得锁
         OrderAccess::release_store_ptr (&_owner, NULL) ;   // drop the lock
         OrderAccess::storeload() ;                         // See if we need to wake a successor
         // code 2:如果没有等待的线程或已经有假定继承人
         if ((intptr_t(_EntryList)|intptr_t(_cxq)) == 0 || _succ != NULL) {
            TEVENT (Inflated exit - simple egress) ;
            return ;
         }
         TEVENT (Inflated exit - complex egress) ;

         // code 3:要执行之后的操作需要重新获得锁,即设置_owner为当前线程
         if (Atomic::cmpxchg_ptr (THREAD, &_owner, NULL) != NULL) {
            return ;
         }
         TEVENT (Exit - Reacquired) ;
      } 
      ...

      ObjectWaiter * w = NULL ;
      // code 4:根据QMode的不同会有不同的唤醒策略,默认为0
      int QMode = Knob_QMode ;
	 
      if (QMode == 2 && _cxq != NULL) {
          // QMode == 2 : cxq中的线程有更高优先级,直接唤醒cxq的队首线程
          w = _cxq ;
          assert (w != NULL, "invariant") ;
          assert (w->TState == ObjectWaiter::TS_CXQ, "Invariant") ;
          ExitEpilog (Self, w) ;
          return ;
      }

      if (QMode == 3 && _cxq != NULL) {
          // 将cxq中的元素插入到EntryList的末尾
          w = _cxq ;
          for (;;) {
             assert (w != NULL, "Invariant") ;
             ObjectWaiter * u = (ObjectWaiter *) Atomic::cmpxchg_ptr (NULL, &_cxq, w) ;
             if (u == w) break ;
             w = u ;
          }
          assert (w != NULL              , "invariant") ;

          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 ;
          }

          // Append the RATs to the EntryList
          // TODO: organize EntryList as a CDLL so we can locate the tail in constant-time.
          ObjectWaiter * Tail ;
          for (Tail = _EntryList ; Tail != NULL && Tail->_next != NULL ; Tail = Tail->_next) ;
          if (Tail == NULL) {
              _EntryList = w ;
          } else {
              Tail->_next = w ;
              w->_prev = Tail ;
          }

          // Fall thru into code that tries to wake a successor from EntryList
      }

      if (QMode == 4 && _cxq != NULL) {
          // 将cxq插入到EntryList的队首
          w = _cxq ;
          for (;;) {
             assert (w != NULL, "Invariant") ;
             ObjectWaiter * u = (ObjectWaiter *) Atomic::cmpxchg_ptr (NULL, &_cxq, w) ;
             if (u == w) break ;
             w = u ;
          }
          assert (w != NULL              , "invariant") ;

          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 ;
          }

          // Prepend the RATs to the EntryList
          if (_EntryList != NULL) {
              q->_next = _EntryList ;
              _EntryList->_prev = q ;
          }
          _EntryList = w ;

          // Fall thru into code that tries to wake a successor from EntryList
      }

      w = _EntryList  ;
      if (w != NULL) {
          // 如果EntryList不为空,则直接唤醒EntryList的队首元素
          assert (w->TState == ObjectWaiter::TS_ENTER, "invariant") ;
          ExitEpilog (Self, w) ;
          return ;
      }

      // EntryList为null,则处理cxq中的元素
      w = _cxq ;
      if (w == NULL) continue ;

      // 因为之后要将cxq的元素移动到EntryList,所以这里将cxq字段设置为null
      for (;;) {
          assert (w != NULL, "Invariant") ;
          ObjectWaiter * u = (ObjectWaiter *) Atomic::cmpxchg_ptr (NULL, &_cxq, w) ;
          if (u == w) break ;
          w = u ;
      }
      TEVENT (Inflated exit - drain cxq into EntryList) ;

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


      if (QMode == 1) {
         // QMode == 1 : 将cxq中的元素转移到EntryList,并反转顺序
         ObjectWaiter * s = NULL ;
         ObjectWaiter * t = w ;
         ObjectWaiter * u = NULL ;
         while (t != NULL) {
             guarantee (t->TState == ObjectWaiter::TS_CXQ, "invariant") ;
             t->TState = ObjectWaiter::TS_ENTER ;
             u = t->_next ;
             t->_prev = u ;
             t->_next = s ;
             s = t;
             t = u ;
         }
         _EntryList  = s ;
         assert (s != NULL, "invariant") ;
      } else {
         // QMode == 0 or QMode == 2‘
         // 将cxq中的元素转移到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 ;
         }
      }


      // _succ不为null,说明已经有个继承人了,所以不需要当前线程去唤醒,减少上下文切换的比率
      if (_succ != NULL) continue;

      w = _EntryList  ;
      // 唤醒EntryList第一个元素
      if (w != NULL) {
          guarantee (w->TState == ObjectWaiter::TS_ENTER, "invariant") ;
          ExitEpilog (Self, w) ;
          return ;
      }
   }
}

После выполнения необходимой оценки повторного входа в блокировку и спин-оптимизации введите основную логику:

code 1Установите владельца на ноль, то есть снимите блокировку, и другие потоки могут получить блокировку в этот момент. Вот оптимизация несправедливой блокировки;

code 2Если в настоящее время нет ожидающих потоков, можно вернуться напрямую, потому что нет необходимости пробуждать другие потоки. Или, если succ не равен нулю, это означает, что уже есть «бодрствующий» поток-наследник, тогда текущему потоку не нужно пробуждать какой-либо поток;

code 3Текущий поток восстанавливает блокировку, потому что очереди cxq и EntryList, а также поток пробуждения должны быть обработаны позже;

code 4В зависимости от QMode будут реализованы разные стратегии пробуждения;

В зависимости от QMode существуют разные методы обработки:

  1. QMode = 2 и cxq не пустой: берем объект ObjectWaiter во главе очереди cxq, вызываем метод ExitEpilog, этот метод разбудит поток объекта ObjectWaiter, а затем немедленно вернется, следующий код выполняться не будет ;
  2. QMode = 3 и cxq не пуст: вставьте очередь cxq в конец EntryList;
  3. QMode = 4 и cxq не пустой: вставить очередь cxq в начало EntryList;
  4. QMode = 0: пока ничего не делать, продолжать смотреть вниз;

Только когда QMode=2 вернется заранее, а когда он будет равен 0, 3 и 4, он продолжит выполнение:

1. Если первый элемент EntryList не пуст, вынуть его и вызвать метод ExitEpilog, который разбудит поток объекта ObjectWaiter, а затем немедленно вернется; 2. Если первый элемент EntryList пуст, поместить все элементы cxq в EntryList, а затем взять первый элемент из EntryList для выполнения метода ExitEpilog, а затем немедленно вернуться;

Приведенное выше резюме QMode относится к этомустатья. Кроме того, о том, как скомпилировать JVM, можно посмотреть в этой статье блоггерастатья, блогер получил образ докера и скомпилировал его для дурака~

QMode по умолчанию равен 0. В сочетании с описанным выше процессом мы можем увидеть такую ​​демонстрацию:

public class SyncDemo {

    public static void main(String[] args) {

        SyncDemo syncDemo1 = new SyncDemo();
        syncDemo1.startThreadA();
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        syncDemo1.startThreadB();
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        syncDemo1.startThreadC();
       

    }

    final Object lock = new Object();


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

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

    public void startThreadC() {
        new Thread(() -> {
            synchronized (lock) {

                System.out.println("C get lock");
            }
        }, "thread-C").start();
    }


}

В соответствии со стратегией по умолчанию после того, как A снимает блокировку, поток C должен сначала получить блокировку. Потому что, когда блокировка получена, текущий поток вставляется в cxqголова, а при снятии блокировки стратегия по умолчанию такова: если EntryList пуст, вставить элементы из cxq в EntryList в исходном порядке и разбудить первый поток. то естьКогда EntryList пуст, более поздний поток первым получает блокировку.. Это отличается от механизма блокировки в JDK.

Разница между Synchronized и ReentrantLock

Принцип ясен, и, кстати, резюмируем несколько отличий между Synchronized и ReentrantLock:

  1. Synchronized — это реализация блокировки на уровне JVM, а ReentrantLock — это реализация блокировки на уровне JDK;
  2. Статус блокировки Synchronized не может быть напрямую оценен в коде, но ReentrantLock может пройтиReentrantLock#isLockedсудить;
  3. Synchronized — это несправедливая блокировка, а ReentrantLock может быть честной или несправедливой;
  4. Синхронизация не может быть прервана, иReentrantLock#lockInterruptiblyметоды могут быть прерваны;
  5. Когда возникает исключение, Synchronized автоматически снимает блокировку (автоматически реализуется javac при компиляции), в то время как ReentrantLock требует, чтобы разработчик снял блокировку в блоке finally;
  6. Существует много форм ReentrantLock, получающих блокировки: например, tryLock(), которая сразу же возвращает информацию об успешном выполнении и ожидание в течение заданного времени для получения блокировки, что является более гибким;
  7. Синхронизируется в особых случаяхДля потоков, которые уже ожидаютЭто более поздний поток, который первым получает блокировку (упомянутую выше), а ReentrantLock предназначен дляТема уже ожидаетПоток, который приходит первым, должен первым получить блокировку;

End

В целом, между тяжеловесной блокировкой Synchronized и реализацией ReentrantLock есть много общего, включая структуру данных, метод приостановки потока и так далее. В повседневном использовании Synchronized достаточно, если нет особых требований. Если у вас есть глубокое понимание реализации одного из двух, вам будет легче понять другой или другие механизмы блокировки, что также является техническим сходством, о котором мы часто говорим.