Comprensión profunda de sincronizado (2) - el principio de implementación sincronizada

¡Acostúmbrate a escribir juntos! Este es el noveno día de mi participación en el "Nuggets Daily New Plan · April Update Challenge", haz clic para ver los detalles del evento .

prefacio

Comprensión profunda de sincronizado (1) - primera comprensión de sincronizado En el artículo anterior, presentamos el rol y el uso de sincronizado.En este artículo, exploraremos cómo sincronizado implementa el bloqueo de objetos.

Monitor

Monitor puede entenderse como una herramienta de sincronización o como un mecanismo de sincronización, que generalmente se describe como un objeto. Como todo es un objeto, todos los objetos de Java nacen monitores, y cada objeto de Java tiene el potencial de convertirse en un Monitor , porque en el diseño de Java, cada objeto de Java sale de la matriz con un bloqueo invisible, se llama interno. bloqueo o bloqueo del monitor .

Sincronizado es bloquear el objeto a través del Monitor (proceso de monitoreo).Primero, veamos el método sincronizado y el método no sincronizado, y veamos la diferencia entre el archivo de código de bytes desensamblado. La prueba de clase es la siguiente, que contiene el método test1() normal y el método test2() que contiene el bloque de código sincronizado sincronizado

public class Test {

    public void test1() {

    }
    public synchronized void test2() {
        synchronized (this) {

        }
    }
}
复制代码

Obtenga el código desensamblado a través de javac y javap:

javac Test.class
javap -c Test
复制代码

El resultado es el siguiente:

imagen.png 可以看到test1()与test2()的代码存在不同,我们主要关注test2()方法增加了monitorenter与monitorexit指令,应该可以猜到这两个指令就是实现线程同步的关键,monitorenter表示同步开始的地方,执行到此指令时,线程会尝试获取对象的锁也就是JVM底层用c++实现的ObjectMonitor对象的所有权,一旦线程获取对象的monitor,在释放该monitor前,其他线程将无法获取到该monitor,从而实现线程之间的同步。而执行到monitorexit时,线程会释放对monitorexit的所有权。 问题:为什么会有两个monitorexit? 前一个monitorexit用于正常情况时退出同步状态,后一个monitorexit用于异常情况退出同步状态。

ObjectMonitor中各个区域线程的流转,其中提到了

ObjectMonitor是JVM底层用c++实现Monitor的对象,java对象头信息中的Mask Word处存储了ObjectMonitor的指针,每个java对象都有一个ObjectMonitor与之关联。我们看看其结构:

//结构体如下
ObjectMonitor::ObjectMonitor() {  
  _header       = NULL;  
  _count       = 0;  
  _waiters      = 0,  
  _recursions   = 0;       //线程的重入次数
  - _count:用来记录该线程获取锁的次数
  
  
  _object       = NULL;  
  _owner        = NULL;    //标识拥有该monitor的线程
  _WaitSet      = NULL;    //等待线程组成的双向循环链表,_WaitSet是第一个节点
  _WaitSetLock  = 0 ;  
  _Responsible  = NULL ;  
  _succ         = NULL ;  
  _cxq          = NULL ;    //多线程竞争锁进入时的单向链表
  FreeNext      = NULL ;  
  _EntryList    = NULL ;    //_owner从该双向循环链表中唤醒线程结点,_EntryList是第一个节点
  _SpinFreq     = 0 ;  
  _SpinClock    = 0 ;  
  OwnerIsThread = 0 ;  
}  

复制代码

首先我们了解下其主要字段的含义:

  • _owner:标识拥有该monitor的线程
  • _WaitSet:等待线程组成的双向循环链表,_WaitSet是第一个节点,存放处于wait状态的线程队列
  • _EntryList:_owner从该双向循环链表中唤醒线程结点,_EntryList是第一个节点,存放处于等待锁block状态的线程队列
  • _recursions:线程的重入次数
  • _count:用来记录该线程获取锁的次数
  • _cxq: 多线程竞争锁进入时的单向链表

当多个线程争抢一个锁时,这些线程首先会进入_cxq队列,而_cxq中的线程会有一些策略筛选,筛选部分有资格获取锁的线程进入_EntryList队列,当_EntryList中某个线程获取到对象的monitor时,当前线程会进入owner区域,并且会将_owner变量置为当前线程,同时_count会加1,如果当前线程是重新进入,则_recursions也会加1,而如果持有monitor的线程因一些条件需要等待(调用wait())时,会释放monitor并进入_WaitSet.处于_WaitSet区域的线程在收到通知(notify/notifyAll方法),对应的线程会从该区域进入到_EntryList区域中。当持有monitor的线程释放monitor时,处于_EntryList中的线程会开始抢占该monitor,但是只能有任意的一个Thread能取得该锁,而其他线程依然在_EntryList中等待下次来抢占到锁之后再执行。

imagen.png

上面我们介绍了ObjectMonitor中各个区域线程的流转,其中提到了_owner与_WaitSet、_EntryList之间会通过wait与notify/notifyAll进行通知或改变状态,下面我们简单介绍下wait()与notify()的实现。

wait()的实现

wait()的实现大致分为以下几步:

  1. 当前线程获取到对象的ObjectMonitor对象(锁),然后调用ObjectMonitor::wait()方法。
  2. ObjectMonitor::wait()方法会将当前线程插入到当前_WaitSet的尾部。
  3. 通过ObjectMonitor::exit方法释放对ObjectMonitor对象(锁)的占用。
  //1.当前线程获取到对象的ObjectMonitor对象(锁),然后调用ObjectMonitor::wait()方法啊。
void ObjectSynchronizer::wait(Handle obj, jlong millis, TRAPS) {
  /*省略 */
  ObjectMonitor* monitor = ObjectSynchronizer::inflate(THREAD, obj());
  DTRACE_MONITOR_WAIT_PROBE(monitor, obj(), THREAD, millis);
  monitor->wait(millis, true, THREAD);
  /*省略*/
}
  //2.ObjectMonitor::wait()方法会将当前线程插入到当前_WaitSet的尾部。
  inline void ObjectMonitor::AddWaiter(ObjectWaiter* node) {
  /*省略*/
  if (_WaitSet == NULL) {
    //_WaitSet为null,就初始化_waitSet
    _WaitSet = node;
    node->_prev = node;
    node->_next = node;
  } else {
    //否则就尾插
    ObjectWaiter* head = _WaitSet ;
    ObjectWaiter* tail = head->_prev;
    assert(tail->_next == head, "invariant check");
    tail->_next = node;
    head->_prev = node;
    node->_next = head;
    node->_prev = tail;
  }
}
  //3.通过ObjectMonitor::exit方法释放对ObjectMonitor对象(锁)的占用。
复制代码

notify()的实现

notify()的实现大致分为以下几步:

  1. 当前持有ObjecyMonitor的线程获取ObjectMonitor锁,调用ObjectMonitor的notify方法。
  2. 将_WaiterSet中第一个线程节点移出。
  3. 将移除的线程节点插入_EntryList尾部。
  //1.当前持有ObjecyMonitor的线程获取ObjectMonitor锁,调用ObjectMonitor的notify方法。
    void ObjectSynchronizer::notify(Handle obj, TRAPS) {
    /*省略*/
    ObjectSynchronizer::inflate(THREAD, obj())->notify(THREAD);
}
    ObjectMonitor * ATTR ObjectSynchronizer::inflate (Thread * Self, oop object) {
    /*省略*/
     if (mark->has_monitor()) {
          ObjectMonitor * inf = mark->monitor() ;
          assert (inf->header()->is_neutral(), "invariant");
          assert (inf->object() == object, "invariant") ;
          assert (ObjectSynchronizer::verify_objmon_isinpool(inf), "monitor is inva;lid");
          return inf 
      }
    /*省略*/ 
      }
    void ObjectMonitor::notify(TRAPS) {
    /*省略*/
    //2.将_WaiterSet中第一个线程节点移出。
    ObjectWaiter * iterator = DequeueWaiter() ;
    //3.将移除的线程节点插入_EntryList尾部。
    /**省略*/
  }

复制代码

结语

Este artículo presenta el principio de implementación de la sincronización del monitor sincronizado y presenta la estructura del ObjectMonitor en la parte inferior de la JVM y cómo realizar la sincronización entre subprocesos y el proceso de subprocesos que compiten por bloqueos.Después de este artículo, todos deberían tener un una comprensión más profunda de sincronizado.

Supongo que te gusta

Origin juejin.im/post/7084947639782866957
Recomendado
Clasificación