In-depth understanding of synchronized (2) - the principle of synchronized implementation

Get into the habit of writing together! This is the 9th day of my participation in the "Nuggets Daily New Plan · April Update Challenge", click to view the details of the event .

foreword

In -depth understanding of synchronized (1) - first understanding of synchronized In the previous article, we introduced the role and usage of synchronized. In this article, we will explore how synchronized implements locking on objects.

Monitor

Monitor can be understood as a synchronization tool or as a synchronization mechanism, which is usually described as an object. Like everything is an object, all Java objects are born monitors, and every Java object has the potential to become a Monitor , because in the design of Java, every Java object comes out of the womb with an invisible lock. , it is called internal lock or Monitor lock .

Synchronized is to lock the object through the Monitor (monitor process). First, let's look at the synchronized method and the non-synchronized method, and see the difference between the disassembled bytecode file. The class Test is as follows, which contains the normal test1() method and the method test2() which contains the synchronized synchronized code block

public class Test {

    public void test1() {

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

        }
    }
}
复制代码

Get the disassembled code through javac and javap:

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

The result is as follows:

image.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中等待下次来抢占到锁之后再执行。

image.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尾部。
    /**省略*/
  }

复制代码

结语

This article introduces the synchronization implementation principle of synchronized-Monitor, and introduces the structure of the ObjectMonitor at the bottom of the JVM and how to realize the synchronization between threads and the process of thread contending for locks. After this article, everyone should have a deeper understanding of synchronized.

Guess you like

Origin juejin.im/post/7084947639782866957