JAVA锁分析之synchronized

一、锁的字节码模板

1.1、先通过一张图说明synchronized (obj) {}的编译后的字节码(偷个懒,参考自这里:https://blog.csdn.net/weixin_42762133/article/details/103241439#synchronized_138)
在这里插入图片描述
1.2、JVM启动的时候将Java指令转成机器码,Java执行的是TemplateTable::monitorenter()生成的机器码

void TemplateTable::monitorenter(){
    __ lock_object(c_rarg1);
}

1.3、如果设置JVM不允许使用偏向锁等,最初生成的指令就是比如说重量级锁(这里就不细究了,是一些繁琐的生成对寄存器操作指令的代码)

void InterpreterMacroAssembler::lock_object(Register lock_reg) {
    if (UseHeavyMonitors) {
        call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), lock_reg);  //锁执行的是InterpreterRuntime::monitorenter
    } else {
        if (UseBiasedLocking) {
            //需要偏向锁
            biased_locking_enter(lock_reg, obj_reg, swap_reg, tmp_reg, false, done, &slow_case);
        }
        call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), lock_reg);
    }
}
二、类结构分析

2.1、理解锁的具体操作之前,要对Object头信息有基础认知(https://www.cnblogs.com/liyefei/p/13413501.html),锁的信息存放在Mark Word
在这里插入图片描述
2.2、Mark Word的基本结构(https://blog.csdn.net/qq_34823218/article/details/107009289),是否偏向锁:0(不支持偏向锁)|1(支持偏向锁)
在这里插入图片描述

三、锁的执行_monitorenter

3.1、入口

IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* thread, BasicObjectLock* elem))
    Handle h_obj(thread, elem->obj());
    if (UseBiasedLocking) {
       ObjectSynchronizer::fast_enter(h_obj, elem->lock(), true, CHECK);
    } else {
       ObjectSynchronizer::slow_enter(h_obj, elem->lock(), CHECK);
    }
IRT_END

3.2、偏向锁:在实际应用运行过程中发现,“锁总是同一个线程持有,很少发生竞争”,也就是说锁总是被第一个占用他的线程拥有,这个线程就是锁的偏向线程。

void ObjectSynchronizer::fast_enter(Handle obj, BasicLock* lock, bool attempt_rebias, TRAPS) {
    if (UseBiasedLocking) {
      //不在安全点  
      if (!SafepointSynchronize::is_at_safepoint()) {
        //尝试获取偏向锁
        BiasedLocking::Condition cond = BiasedLocking::revoke_and_rebias(obj, attempt_rebias, THREAD);
        //偏向锁获取成功,返回继续执行
        if (cond == BiasedLocking::BIAS_REVOKED_AND_REBIASED) {
          return;
        }
      } else {
        //https://blog.csdn.net/dianmi2675/article/details/101491053  
        //Safepoint:从全局观点来看,所有线程必须在GC运行前,在一个safepoint处阻塞(block)。  
        BiasedLocking::revoke_at_safepoint(obj);
      }
    }
    //执行轻量级锁
    slow_enter(obj, lock, THREAD);
}
BiasedLocking::Condition BiasedLocking::revoke_and_rebias(Handle obj, bool attempt_rebias, TRAPS) {
    //读取对象头
    markOop mark = obj->mark();
    //mark=[0       |epoch|age|1|01]  没有锁定线程,并且允许使用偏向锁
    if (mark->is_biased_anonymously() && !attempt_rebias) //!attempt_rebias不希望使用偏向锁
     ->bool is_biased_anonymously() const
        //has_bias_pattern() 返回 true 时代表 markword 的可偏向标志 bit 位为 1 ,且对象头末尾标志为 01。
        //biased_locker() == NULL 返回 true 时代表对象 Mark Word 中 bit field 域存储的 Thread Id 为空。
        //https://blog.csdn.net/lengxiao1993/article/details/81568130
        ->return (has_bias_pattern() && (biased_locker() == NULL));
          ->bool has_bias_pattern() const
            ->return (mask_bits(value(), biased_lock_mask_in_place) == biased_lock_pattern);  //biased_lock_pattern = 5
          ->JavaThread* biased_locker() const
            ->return (JavaThread*) ((intptr_t) (mask_bits(value(), ~(biased_lock_mask_in_place | age_mask_in_place | epoch_mask_in_place))));  
      markOop biased_value       = mark;
      markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age());  //设置为不允许使用偏向锁
      markOop res_mark = obj->cas_set_mark(unbiased_prototype, mark); //通过cas设置
      if (res_mark == biased_value) return BIAS_REVOKED; //偏向锁撤销成功
    //has_bias_pattern() 返回 true 时代表 markword 的可偏向标志 bit 位为 1 ,且对象头末尾标志为 01。
    //mark=[*       |epoch|age|1|01]  没有锁定线程,并且允许使用偏向锁
    else if (mark->has_bias_pattern())
      Klass* k = obj->klass();
      markOop prototype_header = k->prototype_header();
      //对象当前的偏向状态已经过期,并且是不可偏向的,直接设置成已经撤销偏向即可  
      if (!prototype_header->has_bias_pattern())
        return BIAS_REVOKED;  //放弃偏向,走slow_enter(obj, lock, THREAD);
      //实例的epoch和类本身的epoch值不一样,说明它已经过期,也就是说这个对象当前处于未偏向但是可偏向的状态(rebiasable)     
      else if (prototype_header->bias_epoch() != mark->bias_epoch())
        //获取偏向锁成功
        markOop rebiased_prototype = markOopDesc::encode((JavaThread*) THREAD, mark->age(), prototype_header->bias_epoch());
        markOop res_mark = obj->cas_set_mark(rebiased_prototype, mark);
        if (res_mark == biased_value)
          return BIAS_REVOKED_AND_REBIASED;
          
    //锁存在竞争,升级锁并撤销的偏向      
    //启发式策略:https://blog.csdn.net/weixin_39687783/article/details/85058764      
    HeuristicsResult heuristics = update_heuristics(obj(), attempt_rebias);
    if (heuristics == HR_NOT_BIASED) //5.1:偏向状态改成了不需要偏向
      return NOT_BIASED;
    else if (heuristics == HR_SINGLE_REVOKE)//5.2:启发式决定执行单次的撤销
      if (mark->biased_locker() == THREAD && prototype_header->bias_epoch() == mark->bias_epoch()) 
        BiasedLocking::Condition cond = revoke_bias(obj(), false, false, (JavaThread*) THREAD, NULL);
      else
        VM_RevokeBias revoke(&obj, (JavaThread*) THREAD);
        //http://www.uucode.net/201406/jdk-src-vmthread-gc
        //VmThread 是一个很重要的线程。jvm起来的时候 它就会起来。
        //VmThead起来后,直接一个死循环,读取这个操作队列,然后一个一个去执行。
        //VmThead有一个很重要的方法execute(),这个方法不真的执行VM_Operation,而是把一个op推入操作队列。
        VMThread::execute(&revoke);
        return revoke.status_code();
    //6:等到虚拟机运行到safepoint,实际就是执行  VM_BulkRevokeBias 的doit的 bulk_revoke_or_rebias_at_safepoint方法    
    VM_BulkRevokeBias bulk_revoke(&obj, (JavaThread*) THREAD, (heuristics == HR_BULK_REBIAS), attempt_rebias);
    VMThread::execute(&bulk_revoke);
    post_class_revocation_event(&event, obj->klass(), heuristics != HR_BULK_REBIAS);
    return bulk_revoke.status_code();
}

3.3、轻量级锁:
 轻量锁与偏向锁不同的是:
1)轻量级锁每次退出同步块都需要释放锁,而偏向锁是在竞争发生时才释放锁
2)每次进入退出同步块都需要CAS更新对象头
3)争夺轻量级锁失败时,自旋尝试抢占锁

class BasicLock {
    volatile markOop _displaced_header;
    void move_to(oop obj, BasicLock* dest);
}
void ObjectSynchronizer::slow_enter(Handle obj, BasicLock* lock, TRAPS) {
    markOop mark = obj->mark();
    
    //判断mark是否为无锁状态 & 不可偏向(锁标识为01,偏向锁标志位为0) 
    if (mark->is_neutral()) {
        // 保存Mark 到 线程栈 Lock Record 的displaced_header中
        lock->set_displaced_header(mark);
        // CAS 将  Mark Down 更新为 指向 lock 对象的指针,成功则获取到锁 
        if (mark == obj()->cas_set_mark((markOop) lock, mark)) {
            return;
        }
     // 根据对象mark 判断已经有锁  & mark 中指针指的当前线程的Lock Record(当前线程已经获取到了,不必重试获取)   
    } else if (mark->has_locker() && THREAD->is_lock_owned((address)mark->locker())) {
        lock->set_displaced_header(NULL);
        return;
   }
   lock->set_displaced_header(markOopDesc::unused_mark());
   // 锁膨胀
   ObjectSynchronizer::inflate(THREAD, obj(), inflate_cause_monitor_enter)->enter(THREAD);
}

3.4、重量级锁:当竞争线程尝试占用轻量级锁失败多次之后,轻量级锁就会膨胀为重量级锁,重量级线程指针指向竞争线程,竞争线程也会阻塞,等待轻量级线程释放锁后唤醒他。

  ObjectMonitor() {
    _header       = NULL;// displaced object header word - mark
    _waiters      = 0,// number of waiting threads
    _object       = NULL;// backward object pointer - strong root
    _owner        = NULL;// pointer to owning thread OR BasicLock
    _WaitSet      = NULL;// LL of threads wait()ing on the monitor
    FreeNext      = NULL ;// Free list linkage
    _EntryList    = NULL ;// Threads blocked on entry or reentry.
  }
//_EntryList、_cxq的类型
class ObjectWaiter : public StackObj {
    ObjectWaiter * volatile _next;
    ObjectWaiter * volatile _prev;
    Thread*       _thread;
    ParkEvent *   _event;
    ...
}
ObjectMonitor* ObjectSynchronizer::inflate(Thread * Self, oop object, const InflateCause cause) {
    for (;;) {
        const markOop mark = object->mark();
        //判断当前对象是否已经有了监视器,其判断的方式为((value() & monitor_value) != 0),value表示头部的mark_word值,monitor_value=2,用二进制表示就是10
        if (mark->has_monitor()) {//当前对象已有monitor
            ObjectMonitor * inf = mark->monitor();
            return inf;
        }
        
        //如果当前的锁正在膨胀中(轻量级锁膨胀为重量级锁),重新获取mark_word,进入下一次循环
        if (mark == markOopDesc::INFLATING()) {
            ReadStableMark(object);
            continue;
        }
        
        if (mark->has_locker()) { //被轻量级锁标记
            //获取或创建ObjectMonitor
            ObjectMonitor * m = omAlloc(Self);//从当前线程中分配ObjectMonitor
            ->for (;;)
              ->ObjectMonitor * m;
              ->m = Self->omFreeList;
              //a,如果当前线程中omFreeList不为空,分配当前线程omFreeList的头结点。否则进入b
              ->if (m != NULL)
                ->Self->omFreeList = m->FreeNext;
                ->Self->omFreeCount--;
                ->return m;
              //b,如果全局的gFreelist不为空,从gFreelist中分配,否则进入c  
              ->if (gFreeList != NULL)
                ->ObjectMonitor * take = gFreeList;
                ->omRelease(Self, take, false);
                  ->m->FreeNext = Self->omFreeList;
                  ->Self->omFreeList = m;
                ->continue;  
              //c,new一个128大小的ObjectMonitor数组,第0个元素保留, 建立freelist链表,并与gFreeList连通,gFreeList指向新建立的链表.进入下一次循环
              ->size_t neededsize = sizeof(PaddedEnd<ObjectMonitor>) * _BLOCKSIZE;
              ->size_t aligned_size = neededsize + (DEFAULT_CACHE_LINE_SIZE - 1);
              ->void* real_malloc_addr = (void *)NEW_C_HEAP_ARRAY(char, aligned_size, mtInternal);
              ->temp = (PaddedEnd<ObjectMonitor> *) align_up(real_malloc_addr, DEFAULT_CACHE_LINE_SIZE);
              ->for (int i = 1; i < _BLOCKSIZE; i++) //initialize the linked list, each monitor points to its next
                ->temp[i].FreeNext = (ObjectMonitor *)&temp[i+1];  //
              ->emp[0].FreeNext = gBlockList;
              ->temp[_BLOCKSIZE - 1].FreeNext = gFreeList;
              ->gFreeList = temp + 1;
            m->Recycle();
            //cas方式尝试膨胀,如果失败,进入下一次循环
            markOop cmp = object->cas_set_mark(markOopDesc::INFLATING(), mark);
            markOop dmw = mark->displaced_mark_helper();
            m->set_header(dmw);
            m->set_owner(mark->locker());
            m->set_object(object);
            return m;
        }
    }
}
//将线程放入等待队列,并挂起
void ObjectMonitor::enter(TRAPS) {
    Thread * const Self = THREAD;
    void * cur = Atomic::cmpxchg(Self, &_owner, (void*)NULL);
    if (cur == Self) {
        _recursions++;
        return;
    }
    if (Self->is_lock_owned ((address)cur)) {
        _recursions = 1;
       _owner = Self;
       return;
   }
   
/*************** 尝试通过自旋获取锁_S ******************/
   if (Knob_SpinEarly && TrySpin (Self) > 0) return;
     ->int ObjectMonitor::TrySpin(Thread * Self)   
       ->int ctr = Knob_FixedSpin;
       ->while (--ctr >= 0)
         //通过非阻塞的方式尝试加锁
         ->if (TryLock()) return 1;
           ->int Monitor::TryLock()
             ->intptr_t v = _LockWord.FullWord;
             ->for (;;)
               //锁已被占用
               ->if ((v & _LBIT) != 0) return 0;
               //如果CAS成功U未V,如果失败U为成功的那个线程的值
               ->const intptr_t u = Atomic::cmpxchg(v|_LBIT, &_LockWord.FullWord, v);
               ->if (v == u) return 1;
               ->v = u;
         //空转CPU,相当于暂停一会    
         //感觉就是阻塞CPU,同时又不切换CPU执行的线程
         ->SpinPause();
           //nop 也就是多增加几个空的机器周期,一来省电,二来本身spin lock就需要cpu空运行,并且不需要访问内存。
           //https://blog.csdn.net/raintungli/article/details/7109182
           ->SpinPause:
             rep
             nop
             movq   $1, %rax
             ret   
/*************** 尝试通过自旋获取锁_E ******************/

/********** 加入等待队列,并挂起当前线程_S **************/
   for (;;) {
       EnterI(THREAD);
       //创建ObjectWaiter并加入等待队列,_cxq指向最新一条node
       ->ObjectWaiter node(Self);
       ->for (;;)
         ->node._next = nxt = _cxq;
         //如果CAS成功cmpxchg为nxt,如果失败cmpxchg为成功的那个线程的node
         ->if (Atomic::cmpxchg(&node, &_cxq, nxt) == nxt) break;
       ->for (;;)
         //挂起线程
         ->Self->_ParkEvent->park();
         //被唤醒,重新尝试抢锁
         if (TryLock(Self) > 0) break;
   }
/********** 加入等待队列,并挂起当前线程_E **************/
}
四、锁的执行_wait

4.1 注册wait函数

//Object.java
public final native void wait(long timeoutMillis) throws InterruptedException;
static JNINativeMethod methods[] = {
    {"hashCode",    "()I",                    (void *)&JVM_IHashCode},
    {"wait",        "(J)V",                   (void *)&JVM_MonitorWait},
    {"notify",      "()V",                    (void *)&JVM_MonitorNotify},
    {"notifyAll",   "()V",                    (void *)&JVM_MonitorNotifyAll},
    {"clone",       "()Ljava/lang/Object;",   (void *)&JVM_Clone},
};
JNIEXPORT void JNICALL Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls) {
    (*env)->RegisterNatives(env, cls, methods, sizeof(methods)/sizeof(methods[0]));
    ->return functions->RegisterNatives(this,clazz,methods,nMethods);
}

4.2 执行wait函数

JVM_ENTRY(void, JVM_MonitorWait(JNIEnv* env, jobject handle, jlong ms))
  Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
  ObjectSynchronizer::wait(obj, ms, CHECK);
JVM_END
int ObjectSynchronizer::wait(Handle obj, jlong millis, TRAPS) {
  if (UseBiasedLocking) {
    BiasedLocking::revoke_and_rebias(obj, false, THREAD);
  }
  ObjectMonitor* monitor = ObjectSynchronizer::inflate(THREAD, obj(), inflate_cause_wait);
  monitor->wait(millis, true, THREAD);
  return dtrace_waited_probe(monitor, obj, THREAD);
}
void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {
   //省略部分代码
   jt->set_current_waiting_monitor(this);//设置当前正在waiting的监视器
   ObjectWaiter node(Self);//将当前线程封装成一个ObjectWaiter
 
   Thread::SpinAcquire (&_WaitSetLock, "WaitSet - add") ;
   AddWaiter(&node) ;//添加到ObjectMonitor的等待队列中_Waitset
   Thread::SpinRelease(&_WaitSetLock) ;
   exit(true, Self) ;
  //偏向锁,为0才真正需要释放 
  ->if (_recursions != 0) _recursions--; return; 
  //取出阻塞中的线程执行
  ->1:w = _cxq;
  ->2:w = _EntryList;
  ->ExitEpilog(Self, w);
    ->ParkEvent * Trigger = Wakee->_event;
    ->Trigger->unpark(); 
 
   int ret = OS_OK ;
   int WasNotified = 0 ;
   { // State transition wrappers
     OSThread* osthread = Self->osthread();
     OSThreadWaitState osts(osthread, true);
     {
		.......
       if (node._notified == 0) {//如果此时没有被唤醒
         if (millis <= 0) {
            Self->_ParkEvent->park () ;//挂起当前线程,windows下是调用WINAPI函数waitforsingleobject
         } else {
            ret = Self->_ParkEvent->park (millis) ;
         }
       }
		.......
     } 
   .....
}
五、锁的执行_notify

5.1、弹出一个ObjectWaiter放入待执行队列,如果是policy=4,则直接唤醒ObjectWaiter中的线程

void ObjectMonitor::notify(TRAPS) {
    DTRACE_MONITOR_PROBE(notify, this, object(), THREAD);
    //将wait中的线程放入待执行队列,如果是policy=4,则直接唤醒
    INotify(THREAD);
    ->ObjectWaiter * iterator = DequeueWaiter();
    ->ObjectWaiter * list = _EntryList;
    ->if (policy == 0)  //Policy == 0:放入_EntryList队列的排头位置;
    ->else if (policy == 1)  //Policy == 1:放入_EntryList队列的末尾位置;
    ->else if (policy == 2)  //Policy == 2:_EntryList队列为空就放入_EntryList,否则放入_cxq队列的排头位置;请注意把ObjectWaiter的地址写到_cxq变量的时候要用CAS操作,因为此时可能有其他线程正在竞争锁,竞争失败的时候会将自己包装成ObjectWaiter对象加入到_cxq中;
    ->else if (policy == 3)  //Policy == 3:放入_cxq队列中,末尾位置;
   ->else  //Policy等于其他值,立即唤醒ObjectWaiter对应的线程;
     ->ParkEvent * ev = iterator->_event;
     ->ev->unpark(); 
   ->if (policy < 4) 
     ->iterator->wait_reenter_begin(this);  
   OM_PERFDATA_OP(Notifications, inc(1));
}
六、锁的执行_monitorexit
IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorexit(JavaThread* thread, BasicObjectLock* elem))
    Handle h_obj(thread, elem->obj());
    ObjectSynchronizer::slow_exit(h_obj(), elem->lock(), thread);
    ->fast_exit(object, lock, THREAD);
      ->ObjectSynchronizer::inflate(THREAD, object, inflate_cause_vm_internal)->exit(true, THREAD);
        ->void ObjectMonitor::exit(bool not_suspended, TRAPS)
          //偏向锁,为0才真正需要释放 
          ->if (_recursions != 0) _recursions--; return; 
          //取出阻塞中的线程执行
          ->1:w = _cxq;
          ->2:w = _EntryList;
          ->ExitEpilog(Self, w);
            ->ParkEvent * Trigger = Wakee->_event;
            ->Trigger->unpark(); 
IRT_END
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值