Synchronized锁的原理

2021/10/22

synchronized锁的使用

对象锁

  public synchronized  void method(){}
  
   public void method() {
    	synchronized (object) {}
    }

类锁

  public static synchronized  void method(){}
  public void method() {
    	synchronized (XX.class) {}
    }

synchronized内置锁是一种对象锁,作用粒度是对象,作用在普通方法上产生的变化

  public  void test2(){
  }
  
public  void  test3(){
	synchronized(this){}
}
  public static synchronized void test(){
  }
  
  public synchronized void test(){
  }

反编译后得到的代码 javap -verbose

  public void test2();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 6: 0

对于加在代码快的synchronized

<!--代码快-->
  public void test3();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=1
         0: aload_0
         1: dup
         2: astore_1
         3: monitorenter
         4: aload_1
         5: monitorexit
         6: goto          14
         9: astore_2
        10: aload_1
        11: monitorexit
        12: aload_2
        13: athrow
        14: return

多了一些代码,尤其是monitorenter,monitorexit比较显眼,而方法中是通过方法访问标识符实现的

  public static synchronized void tests();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED
    Code:
      stack=0, locals=0, args_size=0
         0: return
      LineNumberTable:
        line 3: 0
        
  public synchronized void test();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 4: 0

可以看到跟普通方法的区别 在 flags: ACC_PUBLIC, ACC_SYNCHRONIZED,多了一个ACC_SYNCHRONIZED标志。方法级别的同步是隐式的,作为方法调用的一部分,当调用一个ACC_SYNCHRONIZED标志的方法,线程也需要先获得monitor锁,然后开始执行方法,方法执行之后再释放monitor锁。如果在方法执行过程中,发生了异常,那么在异常被抛到方法外之前,监视器锁会被自动释放,同步方法和同步代码块都是通过monitor来实现的,对象与monitor一对一,线程可以占有或者释放monitor。

synchronized锁升级及各种状态

synchronized早期完全属于悲观锁,而且完全是重量级锁,一旦牵扯锁竞争,就必定走线程的睡眠与唤醒,这里势必会走内核态与用户态的状态切换,开销非常大,可能睡眠唤醒的代价比代码执行的代价还要高,后期的JDK版本对synchronized进行了优化,有了一个 无锁–>偏向锁–>轻量级锁–>重量级锁的升级过程,除了重量级锁,其他的都不牵扯线程的睡眠唤醒,甚至都可以看做是无锁状态,这里的实现跟对象的头有很大关系,示意图如下

简单看下各个阶段的表现跟原理。

无锁跟偏向锁

其实个人感觉无锁跟偏向锁基本算是一个意思,作用也基本类似,默认偏向锁的开关是开启的,一个对象被创建后,MarkWord字段应该是无锁状态还是偏向锁状态,跟其创建的时机有一些关系,虚拟机启动前几秒创建的都是non-biasable的,

  • 1 虚拟机启动就创建对象的MarkWord值

      OFF  SZ   TYPE DESCRIPTION               VALUE
        0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
        8   4        (object header: class)    0xf80001e5
       12   4        (object alignment gap)   
    
  • 2 虚拟机启动1s后创建对象的MarkWord值

      OFF  SZ   TYPE DESCRIPTION               VALUE
        0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
        8   4        (object header: class)    0xf80001e5
       12   4        (object alignment gap)    
    
  • 3 虚拟机启动3s后创建对象的MarkWord值

      OFF  SZ   TYPE DESCRIPTION               VALUE
        0   8        (object header: mark)     0x0000000000000005 (biasable; age: 0)
        8   4        (object header: class)    0xf80001e5
       12   4        (object alignment gap)    
    
  • 4 虚拟机启动4s后创建对象的MarkWord值

      OFF  SZ   TYPE DESCRIPTION               VALUE
        0   8        (object header: mark)     0x0000000000000005 (biasable; age: 0)
        8   4        (object header: class)    0xf80001e5
       12   4        (object alignment gap)    		 
    

可以看到挺神奇的表现,对象的初始状态的MarkWord跟虚拟机的存活时间有关系,启动前几秒的MarkWord是non-biasable,后面的都是biasable,不过这个并没有太大的影响,无锁跟偏向锁的作用基本是一样,两个状态个人认为基本可以等同。不过,在用synchronized获取对象锁后,表现是略不一样,non-biasable对象的锁会升级为轻量级锁,而biasable的会成为偏向锁状态biased,biasable状态的MarkWord前面会填充线程ID,只有填充色上线程ID,无锁与偏向锁的区别才能体现,没有填充线程ID的biasable与non-biasable是没啥区别,其次可偏向与偏向的差别是是否设置了线程ID

偏向锁与轻量级锁:假设默认从偏向锁开始

偏向锁在运行过程中偏向某个线程,线程获得锁之后,要再次获得锁时,无需做任何同步 [比如CAS自旋],CAS开销很低,就可以再次执行同步代码。这是因为,偏向锁退出同步块时,其实是没有任何操作的,偏向锁标记依旧存在,线程ID依旧是当前线程,这就规避了频繁CAS设置,CAS复原。具体加锁、释放可简化为

  • 如果第一次使用锁,则通过CAS设置为当前线程ID,并从biasable转换为biased
  • 执行同步代码区,执行结束后,不释放偏向锁。
  • 再次获取锁的时候会判断是不是当前线程,或者是不是初始状态,如果不是,则说明存在其他线程竞争
  • 在安全点挂起偏向锁线程,释放偏向锁,并膨胀为轻量级锁
  • 被阻塞在安全点的线程继续往下通过CAS竞争轻量级锁
  • 成功则继续执行,失败升级为重量级锁
  • 5 biasable对象被synchronized获取对象锁之后,对象的MarkWord值会升级为biased偏向锁

      OFF  SZ   TYPE DESCRIPTION               VALUE
        0   8        (object header: mark)     0x00007fdf6910d005 (biased: 0x0000001ff7da4434; epoch: 0; age: 0)
        8   4        (object header: class)    0xf80001e5
       12   4        (object alignment gap)    
    

偏向锁也会用到Lock Record,可重入性就是利用这个实现的,同时Lock Record也被用在锁升级的过程中,每次进入同步块的时候都会在栈中找到第一个可用(即栈中最高的)的Lock Record,将其obj字段指向锁对象,在偏向锁的获取中,每次进入同步块的时候都会在栈中找到第一个可用(即栈中最高的)的Lock Record,将其obj字段指向锁对象。每次解锁的时候都会把最低的Lock Record移除掉,所以可以通过遍历线程栈中的Lock Record来判断是否还在同步块中。存在竞争的时候,会根据是否在同步代码块决定是否直接撤销偏向锁,如果是在同步代码去则直接升级成轻量级锁,并设置给运行的线程,否则,先恢复成无锁状态后,再膨胀成轻量级锁,之后唤起之前被挂起的偏向锁线程,同时其他线程通过CAS+自旋争取轻量级锁。

non-biasable对象被synchronized利用自旋+CAS的方式来抢锁获取对象锁,之后MarkWord值会直接成为tink lock,这个不在这里讨论,因为它类似biasable,只不过在退出同步代码快的时候,它需需要通过CAS释放锁

	OFF  SZ   TYPE DESCRIPTION               VALUE
	  0   8        (object header: mark)     0x000000030abbc9e8 (thin lock: 0x000000030abbc9e8)
	  8   4        (object header: class)    0xf80001e5
	 12   4        (object alignment gap)   

获取偏向锁的线程不会主动释放偏向锁,即使是退出同步代码区,在遇到其他线程尝试竞争锁时,偏向锁线程才会释放锁。如果升级后的轻量级锁,仍然竞争失败,则直接升级为fat lock ,偏向锁感觉是一次性的,升级为轻量级之后,就打破原来的模型,不会再恢复了,偏向与轻量级最大的区别是是否主动释放锁。

轻量级锁与重量级锁

轻量级锁假定的模型是线程交替使用资源,只要没有同时申请锁,就不会升级为重量级锁,它利用自旋代替内核态与用户态的状态切换,降低开销,

  • 6 tink lock如果竞争失败,MarkWord值会升级为fat lock

      OFF  SZ   TYPE DESCRIPTION               VALUE
        0   8        (object header: mark)     0x00007ff14c824e8a (fat lock: 0x00007ff14c824e8a)
        8   4        (object header: class)    0xf80001e5
       12   4        (object alignment gap)  		  
    

轻量级锁的申请过程是:如果是偏向锁状态,则撤销偏向锁升级,或者直接升级为轻量级锁,如上面所述,不过上述在安全节点不需要CAS。如果本身是无锁状态,则升级+获取一体,首先在当前线程栈帧中建立一个Lock Record,用于拷贝锁对象的 Mark Word ,拷贝成功后,利用CAS 尝试将对象的 Mark Word 更新为新的 Lock Record 的指针,并将 Lock Record里的 owner 指针指向对象的 Mark Word,如果成功了,则这个线程就拥有了该对象的锁,并且暂时处于轻量级锁定状态,如果失败,则说明多个线程竞争锁,轻量级锁就要膨胀为重量级锁,锁标志的状态值变为 10 ,Mark Word中存储的就是指向重量级锁的指针,后面等待锁的线程也要进入阻塞状态,轻重的最大区别是是否借助系统资源,并涉及内核态及用户态的切换。

tips :对象的Lock Record 指向哪个线程的栈帧,哪个线程就拥有该轻量级锁。

轻量级锁的可重入

每次获取轻量级锁,都会新建一个Lock Record,但是只有最开始的Lock Record填充了锁对象的加锁前的mark word,Displaced Mark word有值,之后可重入的分配一个Displaced Mark word为null,因为没必要浪费资源存储无用的东西,最后的哪个退出锁的Displaced Mark word才有用,利用Lock Record列表实现可重入,其实偏向锁也是这么做的,只是偏向锁的Lock Record没有Displaced Mark word。

monitorenter指令解析

InterpreterRuntime:: monitorenter

IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* thread, BasicObjectLock* elem))
#ifdef ASSERT
  thread->last_frame().interpreter_frame_verify_monitor(elem);
#endif
  if (PrintBiasedLockingStatistics) {
    Atomic::inc(BiasedLocking::slow_path_entry_count_addr());
  }
  Handle h_obj(thread, elem->obj());
  assert(Universe::heap()->is_in_reserved_or_null(h_obj()),
         "must be NULL or an object"); 
//如果使用偏向锁,就进入Fast_enter,避免不必要的锁膨胀,如果不是偏向锁,就进入slow_enter,也就是锁升级
  if (UseBiasedLocking) {
    // Retry fast entry if bias is revoked to avoid unnecessary inflation
    ObjectSynchronizer::fast_enter(h_obj, elem->lock(), true, CHECK);
  } else {
    ObjectSynchronizer::slow_enter(h_obj, elem->lock(), CHECK);
  }
  assert(Universe::heap()->is_in_reserved_or_null(elem->obj()),
         "must be NULL or an object");
#ifdef ASSERT
  thread->last_frame().interpreter_frame_verify_monitor(elem);
#endif
IRT_END

先判断是否使用偏向锁,如果用的话,先进入fast_enter偏向锁逻辑,利用CAS走无锁编程的逻辑,否则走slow_enter。

void ObjectSynchronizer::fast_enter(Handle obj, BasicLock* lock, bool attempt_rebias, TRAPS) {
 if (UseBiasedLocking) {
     //如果使用偏向锁,那么尝试偏向
    if (!SafepointSynchronize::is_at_safepoint()) {
        //如果线程不在安全点,那么就尝试BiasedLocking::revoke_and_rebias实现撤销并且重偏向
      BiasedLocking::Condition cond = BiasedLocking::revoke_and_rebias(obj, attempt_rebias, THREAD);
        //偏向成功,直接返回
      if (cond == BiasedLocking::BIAS_REVOKED_AND_REBIASED) {
        return;
      }
    } else {
      assert(!attempt_rebias, "can not rebias toward VM thread");
        //进入这里说明线程在安全点并且撤销偏向
      BiasedLocking::revoke_at_safepoint(obj);
    }
    assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now");
 }
//使用兜底方案,slow_enter
 slow_enter (obj, lock, THREAD) ;
}

slow_enter,调用cmpxchg进行自旋(cmpxchg),如果成功则返回,说明获得轻量级锁;如果不成功,就进入锁膨胀

	 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对象中
    lock->set_displaced_header(mark);
    //通过cmpxchg进入自旋替换对象头为lock对象地址,如果替换成功则直接返回,表明获得了轻量级锁,不然继续自旋
    if (mark == (markOop) Atomic::cmpxchg_ptr(lock, obj()->mark_addr(), mark)) {
      TEVENT (slow_enter: release stacklock) ;
      return ;
    }
    // 否则判断当前对象是否上锁,并且当前线程是否是锁的占有者,如果是markword的指针指向栈帧中的LR,则重入
  } 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;
  }
​
#if 0
  // The following optimization isn't particularly useful.
  if (mark->has_monitor() && mark->monitor()->is_entered(THREAD)) {
    lock->set_displaced_header (NULL) ;
    return ;
  }
#endif
​
  // 代码执行到这里,说明有多个线程竞争轻量级锁,轻量级锁通过`inflate`进行膨胀升级为重量级锁
  lock->set_displaced_header(markOopDesc::unused_mark());
  ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD);
}  这里轻量级锁是通过BasicLock对象来实现的,在线程JVM栈中产生一个LR(lock Record)的栈桢,然后他们两个CAS竞争锁,成功的,就会在Markword中记录一个指针(62位),这个指针指向竞争成功的线程的LR,另外一个线程CAS自旋继续竞争,等到前面线程用完了,才进入。这就是自旋锁的由来。

ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD);则是进行锁膨胀,升级为重量级锁。主要分为两部,其中inflate用于获取监视器monitor,enter用于抢占锁

ObjectMonitor * ATTR ObjectSynchronizer::inflate (Thread * Self, oop object) {
  // Inflate mutates the heap ...
  // Relaxing assertion for bug 6320749.
  assert (Universe::verify_in_progress() ||
          !SafepointSynchronize::is_at_safepoint(), "invariant") ;
​
  for (;;) { //通过无意义的循环实现自旋操作
      const markOop mark = object->mark() ;
      assert (!mark->has_bias_pattern(), "invariant") ;
​
      if (mark->has_monitor()) {//has_monitor是markOop.hpp中的方法,如果为true表示当前锁已经是重量级锁了
          ObjectMonitor * inf = mark->monitor() ;//获得重量级锁的对象监视器直接返回
          assert (inf->header()->is_neutral(), "invariant");
          assert (inf->object() == object, "invariant") ;
          assert (ObjectSynchronizer::verify_objmon_isinpool(inf), "monitor is invalid");
          return inf ;
      }
​
      if (mark == markOopDesc::INFLATING()) {//膨胀等待,表示存在线程正在膨胀,通过continue进行下一轮的膨胀
         TEVENT (Inflate: spin while INFLATING) ;
         ReadStableMark(object) ;
         continue ;
      }
​
      if (mark->has_locker()) {//表示当前锁为轻量级锁,以下是轻量级锁的膨胀逻辑
          ObjectMonitor * m = omAlloc (Self) ;//获取一个可用的ObjectMonitor
          // Optimistically prepare the objectmonitor - anticipate successful CAS
          // We do this before the CAS in order to minimize the length of time
          // in which INFLATING appears in the mark.
          m->Recycle();
          m->_Responsible  = NULL ;
          m->OwnerIsThread = 0 ;
          m->_recursions   = 0 ;
          m->_SpinDuration = ObjectMonitor::Knob_SpinLimit ;   // Consider: maintain by type/class
          /**将object->mark_addr()和mark比较,如果这两个值相等,则将object->mark_addr()
          改成markOopDesc::INFLATING(),相等返回是mark,不相等返回的是object->mark_addr()**/
                     markOop cmp = (markOop) Atomic::cmpxchg_ptr (markOopDesc::INFLATING(), object->mark_addr(), mark) ;
          if (cmp != mark) {//CAS失败
             omRelease (Self, m, true) ;//释放监视器
             continue ;       // 重试
          }
​
          markOop dmw = mark->displaced_mark_helper() ;
          assert (dmw->is_neutral(), "invariant") ;
​
          //CAS成功以后,设置ObjectMonitor相关属性
          m->set_header(dmw) ;
​
​
          m->set_owner(mark->locker());
          m->set_object(object);
          // TODO-FIXME: assert BasicLock->dhw != 0.
​
​
          guarantee (object->mark() == markOopDesc::INFLATING(), "invariant") ;
          object->release_set_mark(markOopDesc::encode(m));
​
​
          if (ObjectMonitor::_sync_Inflations != NULL) ObjectMonitor::_sync_Inflations->inc() ;
          TEVENT(Inflate: overwrite stacklock) ;
          if (TraceMonitorInflation) {
            if (object->is_instance()) {
              ResourceMark rm;
              tty->print_cr("Inflating object " INTPTR_FORMAT " , mark " INTPTR_FORMAT " , type %s",
                (void *) object, (intptr_t) object->mark(),
                object->klass()->external_name());
            }
          }
          return m ; //返回ObjectMonitor
      }
      //如果是无锁状态
      assert (mark->is_neutral(), "invariant");
      ObjectMonitor * m = omAlloc (Self) ; ////获取一个可用的ObjectMonitor
      //设置ObjectMonitor相关属性
      m->Recycle();
      m->set_header(mark);
      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
      /**将object->mark_addr()和mark比较,如果这两个值相等,则将object->mark_addr()
          改成markOopDesc::encode(m),相等返回是mark,不相等返回的是object->mark_addr()**/
      if (Atomic::cmpxchg_ptr (markOopDesc::encode(m), object->mark_addr(), mark) != mark) {
          //CAS失败,说明出现了锁竞争,则释放监视器重行竞争锁
          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.
      }
​
      if (ObjectMonitor::_sync_Inflations != NULL) ObjectMonitor::_sync_Inflations->inc() ;
      TEVENT(Inflate: overwrite neutral) ;
      if (TraceMonitorInflation) {
        if (object->is_instance()) {
          ResourceMark rm;
          tty->print_cr("Inflating object " INTPTR_FORMAT " , mark " INTPTR_FORMAT " , type %s",
            (void *) object, (intptr_t) object->mark(),
            object->klass()->external_name());
        }
      }
      return m ; //返回ObjectMonitor对象
  }
}

可以都看到返回值是ObjectMonitor,

ObjectMonitor对象锁的原理

重量级MarkWord锁标识位为10,指针指向的是 monitor 对象的起始地址,monitor对象可以与对象一起创建销毁,或者当线程试图获取对象锁时自动生成,一旦某个monitor被某个线程持有后,monitor便处于锁定状态,实现以是ObjectMonitor。ObjectMonitor的实现可以简单看下:

//结构体如下
ObjectMonitor::ObjectMonitor() {  
  _header       = NULL;  
  _count       = 0;  
  _waiters      = 0,  
  _recursions   = 0;       //线程重入次数
  _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 ;  
} 
  • 监控区(Entry Set): 锁已被其他线程获取,等待获取锁的线程就进入Monitor对象的监控区
  • 待授权区(Wait Set):获取到锁,但是调用了wait方法进入待授权区[必须等待Notify重新进去监控区]

在锁已经被其它线程拥有的时候,请求锁的线程回进入了对象锁的entry set区域,一旦锁被释放,entryset区域的线程都会抢占锁,只能有任意的一个Thread能取得该锁,其他线程重新等待锁释放。如果调用wait方法,则线程进入Wait Set,等待Notify/notifyAll,线程先转移到wait set,等到锁释放,再竞争,而其enter函数

void ATTR ObjectMonitor::enter(TRAPS) {
  Thread * const Self = THREAD ;
  void * cur ;
  //通过CAS操作尝试把monitor的_owner字段设置为当前线程
  cur = Atomic::cmpxchg_ptr (Self, &_owner, NULL) ;
  //获取锁失败
  if (cur == NULL) {
     assert (_recursions == 0   , "invariant") ;
     assert (_owner      == Self, "invariant") ;
     return ;
  }
//如果之前的_owner指向该THREAD,那么该线程是重入,_recursions++
  if (cur == Self) {
     _recursions ++ ;
     return ;
  }
//如果当前线程是第一次进入该monitor,设置_recursions为1,_owner为当前线程
  if (Self->is_lock_owned ((address)cur)) {
  <!--这里其他线程进不来-->
    assert (_recursions == 0, "internal state error");
    _recursions = 1 ;   //_recursions标记为1
    _owner = Self ;     //设置owner
    OwnerIsThread = 1 ;
    return ;
  }
  
  <!--否则竞争失败挂起自己-->
  ...
    jt->java_suspend_self();

主要是通过CAS判断当前线程的指针和监视器的_owner比较替换,如果成功了直接返回,如果失败了就判断当前线程是不是占用了监视器,如果是,则是重入的,次数加1,再开始竞争,竞争的方式有自旋竞争(TrySpin)和等待竞争(EnterI)。

ObjectMonitor与wait notify

为什么wait必须在syncronized中调用,wait是调用的某个锁的wait函数,为了保证能够执行不混乱, 必须在syncronized调用

为什么wait会释放锁

  //1.调用ObjectSynchronizer::wait方法
void ObjectSynchronizer::wait(Handle obj, jlong millis, TRAPS) {
  /*省略 */
  //2.获得Object的monitor对象(即内置锁)
  ObjectMonitor* monitor = ObjectSynchronizer::inflate(THREAD, obj());
  DTRACE_MONITOR_WAIT_PROBE(monitor, obj(), THREAD, millis);
  //3.调用monitor的wait方法
  monitor->wait(millis, true, THREAD);
  /*省略*/
}
  //4.在wait方法中调用addWaiter方法
  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;
  }
}
  //5.然后在ObjectMonitor::exit释放锁,接着 thread_ParkEvent->park  也就是wait

总结:通过object获得内置锁(objectMonitor),通过内置锁将Thread封装成OjectWaiter对象,然后addWaiter将它插入以_waitSet为首结点的等待线程链表中去,最后释放锁。

notify方法的底层实现

  //1.调用ObjectSynchronizer::notify方法
    void ObjectSynchronizer::notify(Handle obj, TRAPS) {
    /*省略*/
    //2.调用ObjectSynchronizer::inflate方法
    ObjectSynchronizer::inflate(THREAD, obj())->notify(THREAD);
}
    //3.通过inflate方法得到ObjectMonitor对象
    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 
      }
    /*省略*/ 
      }
    //4.调用ObjectMonitor的notify方法
    void ObjectMonitor::notify(TRAPS) {
    /*省略*/
    //5.调用DequeueWaiter方法移出_waiterSet第一个结点
    ObjectWaiter * iterator = DequeueWaiter() ;
    //6.后面省略是将上面DequeueWaiter尾插入_EntrySet的操作
    /**省略*/
  } 总结:通过object获得内置锁(objectMonitor),调用内置锁的notify方法,通过_waitset结点移出等待链表中的首结点,将它置于_EntrySet中去,等待获取锁。注意:notifyAll根据policy不同可能移入_EntryList或者_cxq队列中,此处不详谈。

Monitorexit 与锁的释放

可释放的锁都会锁撤到non-biasable状态。轻量级锁、重量级锁使用完毕之后,都会释放,并恢复到non-biasable的无锁状态,偏向锁无法恢复。

synchroniz关键字也能保证可见性

  • 当ThreadA释放锁M时,它所写过的变量(比如,x和y,存在它工作内存中的)都会同步到主存中,而当ThreadB在申请同一个锁M时
  • ThreadB的工作内存会被设置为无效,然后ThreadB会重新从主存中加载它要访问的变量到它的工作内存中(这时x=1,y=1,是ThreadA中修改过的最新的值)。通过这样的方式来实现ThreadA到ThreadB的线程间的通信。

总结

synchronized底层其实跟ReetrantLock核心也差不多,最底层的中量级锁,也是依赖线程睡眠唤醒+队列+CAS操作等实现的

参考文档

https://www.cnblogs.com/sunddenly/articles/15106247.html

https://www.cnblogs.com/hongdada/p/14513036.html

monitorenter源码 从 Monitorenter 源码看 Synchronized 锁优化的过程 [https://www.cnblogs.com/hongdada/p/14513036.html] (内置锁(ObjectMonitor))

Search

    Table of Contents