并发编程6

锁的膨胀过程2

重偏向不同于锁重入

  • 重偏向是第一次偏向锁已经释放了,再一次加偏向锁,就是一个代码块里有两个相邻的synchronized块
  • 锁重入就是synchronized里面再套一个synchronized

一个线程进入synchronized并退出后对象头的变化

  • 轻量锁加锁有个前置的逻辑
  • 首先第一个线程获取锁成功后,对象头改为:线程id+101
  • 当这个线程执行完退出synchronized块后,对象头改为:000…0001

synchronized源码分析

  • 并发编程5中用到的openjdk源码是jdk8,其中有很多cas操作,比如轻量锁替换lock record的操作—Atomic::cmpxchg_ptr(entry, lockee->mark_addr(), displaced)

  • 下面的代码是jdk12,对很多cas操作进行了进一步封装,更加看得懂

  • 比如上面轻量锁替换锁记录的操作被更新成—lockee->cas_set_mark((markOop)entry, displaced)

  • CASE(_monitorenter): {
        oop lockee = STACK_OBJECT(-1);
        // derefing's lockee ought to provoke implicit null check
        CHECK_NULL(lockee);
        // find a free monitor or one already allocated for this object
        // if we find a matching object then we need a new monitor
        // since this is recursive enter
        BasicObjectLock* limit = istate->monitor_base();
        BasicObjectLock* most_recent = (BasicObjectLock*) istate->stack_base();
        BasicObjectLock* entry = NULL;
        while (most_recent != limit ) {
            if (most_recent->obj() == NULL) entry = most_recent;
            else if (most_recent->obj() == lockee) break;
            most_recent++;
        }
        if (entry != NULL) {
            // entry是每个线程私有栈帧中都会创建的
            // entry表示lock record
            // 将lock record里面的obj reference指向mark word
            entry->set_obj(lockee);
            int success = false;
            uintptr_t epoch_mask_in_place = (uintptr_t)markOopDesc::epoch_mask_in_place;
    
            markOop mark = lockee->mark();
            intptr_t hash = (intptr_t) markOopDesc::no_hash;
            // implies UseBiasedLocking
            // 是否支持偏向锁
            if (mark->has_bias_pattern()) {
                uintptr_t thread_ident;
                uintptr_t anticipated_bias_locking_value;
                // 获取当前线程的id
                thread_ident = (uintptr_t)istate->thread();
                // 这个计算比较复杂
                // 如果这个值等于0,说明当前线程id和mark word里面存的线程id是相同的
                anticipated_bias_locking_value =
                    (((uintptr_t)lockee->klass()->prototype_header() | thread_ident) ^ (uintptr_t)mark) &
                    ~((uintptr_t) markOopDesc::age_mask_in_place);
    			// 同一个线程获取偏向锁
                if  (anticipated_bias_locking_value == 0) {
                    // already biased towards this thread, nothing to do
                    // 进入次数加1
                    if (PrintBiasedLockingStatistics) {
                        (* BiasedLocking::biased_lock_entry_count_addr())++;
                    }
                    success = true;
                }
                // 是否不可偏向
                else if ((anticipated_bias_locking_value & markOopDesc::biased_lock_mask_in_place) != 0) {
                    // try revoke bias
                    markOop header = lockee->klass()->prototype_header();
                    if (hash != markOopDesc::no_hash) {
                        header = header->copy_set_hash(hash);
                    }
                    if (lockee->cas_set_mark(header, mark) == mark) {
                        if (PrintBiasedLockingStatistics)
                            (*BiasedLocking::revoked_lock_entry_count_addr())++;
                    }
                }
                // 是否过期,如果过期需要重偏向
                else if ((anticipated_bias_locking_value & epoch_mask_in_place) !=0) {
                    // try rebias
                    markOop new_header = (markOop) ( (intptr_t) lockee->klass()->prototype_header() | thread_ident);
                    if (hash != markOopDesc::no_hash) {
                        new_header = new_header->copy_set_hash(hash);
                    }
                    // 就是对象头被别人改了
                    if (lockee->cas_set_mark(new_header, mark) == mark) {
                        if (PrintBiasedLockingStatistics)
                            (* BiasedLocking::rebiased_lock_entry_count_addr())++;
                    }
                    else {
                        CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
                    }
                    success = true;
                }
                // 进到else
                // 1.匿名偏向(必须是101的状态)  2.
                // 首先肯定不是自己 
                // 不是自己有两种情况 1.压根没有偏向 2.
                else {
                    // try to bias towards thread in case object is anonymously biased
                    markOop header = (markOop) ((uintptr_t) mark & ((uintptr_t)markOopDesc::biased_lock_mask_in_place |
                                                                    (uintptr_t)markOopDesc::age_mask_in_place |
                                                                    epoch_mask_in_place));
                    if (hash != markOopDesc::no_hash) {
                        header = header->copy_set_hash(hash);
                    }
                    markOop new_header = (markOop) ((uintptr_t) header | thread_ident);
                    // debugging hint
                    // 看看是否是匿名偏向 如果是则偏向自己
                    DEBUG_ONLY(entry->lock()->set_displaced_header((markOop) (uintptr_t) 0xdeaddead);)
                        if (lockee->cas_set_mark(new_header, header) == header) {
                            if (PrintBiasedLockingStatistics)
                                (* BiasedLocking::anonymously_biased_lock_entry_count_addr())++;
                        }
                    else {
                        // monitorenter 首先会去进行偏向锁撤销 撤销的时候判断是否在同步块中;如果在则改成轻量锁,否则返回
                        CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
                    }
                    success = true;
                }
            }
    
            // traditional lightweight locking
            if (!success) {
                // 压根就不是偏向锁 偏向失败
                // 生成一个无锁的mark word 001
                markOop displaced = lockee->mark()->set_unlocked();
                entry->lock()->set_displaced_header(displaced);
                // call_vm这个值永远是false
                bool call_vm = UseHeavyMonitors;
                // openjdk8----Atomic::cmpxchg_ptr(entry, lockee->mark_addr(), displaced) != displaced  如果这整个是false,表示加锁成功
                // openjdk12---lockee->cas_set_mark((markOop)entry, displaced) != displaced     如果这整个是false,表示加锁成功
                
                // 这个cas操作是对比lockee->cas_set_mark((markOop)entry, displaced)里面的displaced和内存中mark word中的是一样吗,如果一样则更新为(markOop)entry  然后返回(markOop)entry  此时不等于displaced,所以cas更新成功,但是if逻辑体的结果是false
                // 如果此时对象头是无锁 则修改entry的地址 为什么失败?
                if (call_vm || lockee->cas_set_mark((markOop)entry, displaced) != displaced) {
                    // Is it simple recursive case?
                    if (!call_vm && THREAD->is_lock_owned((address) displaced->clear_lock_bits())) {
                        entry->lock()->set_displaced_header(NULL);
                    } else {
                        CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
                    }
                }
            }
            // 执行CPU下一条指令
            UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
        } else {
            istate->set_msg(more_monitors);
            UPDATE_PC_AND_RETURN(0); // Re-execute
        }
    }
    

场景1:线程1获取锁,并释放锁,线程2进来加锁

  • 必须清楚一点,对应头只有一个,多个线程对应对个线程私有栈,正常情况每个线程私有栈只有一个lock recod,当有锁重入情况时,同一个线程会有多个lock record

  • 线程1获取锁成功,对象头改为:线程id+101

  • 线程1释放锁,对象头改为:00…0001

  • 线程2来加锁,偏向锁的逻辑都不会进入,success=false,进入轻量锁加锁逻辑,对象头改为:lock record的指针地址+00

场景2:t1来加锁,直接加锁成功

  • CASE(_monitorenter): {
        oop lockee = STACK_OBJECT(-1);
        // derefing's lockee ought to provoke implicit null check
        CHECK_NULL(lockee);
        // find a free monitor or one already allocated for this object
        // if we find a matching object then we need a new monitor
        // since this is recursive enter
        BasicObjectLock* limit = istate->monitor_base();
        BasicObjectLock* most_recent = (BasicObjectLock*) istate->stack_base();
        BasicObjectLock* entry = NULL;
        while (most_recent != limit ) {
            if (most_recent->obj() == NULL) entry = most_recent;
            else if (most_recent->obj() == lockee) break;
            most_recent++;
        }
        if (entry != NULL) {
            // entry表示此处线程私有栈里新生成的lock record
            // 并将lock record里面的obj reference指向mark word
            // 此时是第一次,mark word是101
            entry->set_obj(lockee);
            int success = false;
            uintptr_t epoch_mask_in_place = (uintptr_t)markOopDesc::epoch_mask_in_place;
    
            markOop mark = lockee->mark();
            intptr_t hash = (intptr_t) markOopDesc::no_hash;
            // implies UseBiasedLocking
            if (mark->has_bias_pattern()) {
                uintptr_t thread_ident;
                uintptr_t anticipated_bias_locking_value;
                thread_ident = (uintptr_t)istate->thread();
                anticipated_bias_locking_value =
                    (((uintptr_t)lockee->klass()->prototype_header() | thread_ident) ^ (uintptr_t)mark) &
                    ~((uintptr_t) markOopDesc::age_mask_in_place);
    
                if  (anticipated_bias_locking_value == 0) {
                    // already biased towards this thread, nothing to do
                    if (PrintBiasedLockingStatistics) {
                        (* BiasedLocking::biased_lock_entry_count_addr())++;
                    }
                    success = true;
                }
                // 是否不可偏向
                else if ((anticipated_bias_locking_value & markOopDesc::biased_lock_mask_in_place) != 0) {
                    // try revoke bias
                    markOop header = lockee->klass()->prototype_header();
                    if (hash != markOopDesc::no_hash) {
                        header = header->copy_set_hash(hash);
                    }
                    if (lockee->cas_set_mark(header, mark) == mark) {
                        if (PrintBiasedLockingStatistics)
                            (*BiasedLocking::revoked_lock_entry_count_addr())++;
                    }
                }
                // 是否过期
                else if ((anticipated_bias_locking_value & epoch_mask_in_place) !=0) {
                    // try rebias
                    markOop new_header = (markOop) ( (intptr_t) lockee->klass()->prototype_header() | thread_ident);
                    if (hash != markOopDesc::no_hash) {
                        new_header = new_header->copy_set_hash(hash);
                    }
                    if (lockee->cas_set_mark(new_header, mark) == mark) {
                        if (PrintBiasedLockingStatistics)
                            (* BiasedLocking::rebiased_lock_entry_count_addr())++;
                    }
                    else {
                        CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
                    }
                    success = true;
                }
                // 1.匿名偏向
                // 2.偏向别人
                else {
                    // try to bias towards thread in case object is anonymously biased
                    // 创建一个匿名偏向的header
                    // 000....000101
                    markOop header = (markOop) ((uintptr_t) mark & ((uintptr_t)markOopDesc::biased_lock_mask_in_place |
                                                                    (uintptr_t)markOopDesc::age_mask_in_place |
                                                                    epoch_mask_in_place));
                    // 考虑是否有hash
                    if (hash != markOopDesc::no_hash) {
                        header = header->copy_set_hash(hash);
                    }
                    // 创建一个偏向自己的mark
                    // t1的线程id+101
                    markOop new_header = (markOop) ((uintptr_t) header | thread_ident);
                    // debugging hint
                    DEBUG_ONLY(entry->lock()->set_displaced_header((markOop) (uintptr_t) 0xdeaddead);)
                        // 匿名偏向就是无所可偏向
                        // 看看是否是匿名偏向 如果是则偏向自己
                        // 如果mark word中是匿名偏向---header,则更新为t1线程id+101---new
                        if (lockee->cas_set_mark(new_header, header) == header) {
                            if (PrintBiasedLockingStatistics)
                                (* BiasedLocking::anonymously_biased_lock_entry_count_addr())++;
                        }
                    // 不是匿名偏向,就会失败
                    // monitorenter 首先回去进行偏向锁撤销,撤销的时候判断是否在同步块中,如果在则改成轻量锁,否则返回
                    else {
                        CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
                    }
                    success = true;
                }
            }
    
            // traditional lightweight locking
            if (!success) {
                markOop displaced = lockee->mark()->set_unlocked();
                entry->lock()->set_displaced_header(displaced);
                bool call_vm = UseHeavyMonitors;
                if (call_vm || lockee->cas_set_mark((markOop)entry, displaced) != displaced) {
                    // Is it simple recursive case?
                    if (!call_vm && THREAD->is_lock_owned((address) displaced->clear_lock_bits())) {
                        entry->lock()->set_displaced_header(NULL);
                    } else {
                        CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
                    }
                }
            }
            UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
        } else {
            istate->set_msg(more_monitors);
            UPDATE_PC_AND_RETURN(0); // Re-execute
        }
    }
    

匿名偏向锁

1.第一个进入锁的线程,就是匿名偏向(101)(无锁可偏向)的这种情况。

不考虑偏向关闭的情况。

1.1会进入到最后一个else里面,匿名偏向判断里面,

  • 首先创建匿名偏向的header(在cpu内存里面存在一个匿名偏向),然后判断有没有哈希,
  • 又创建一个newheade(偏向t1的mark),当前线程t1Id加上101,
  • lockee -> cas_set_mark(new_header,header),

如果匿名偏向失败进入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");
      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(synchronized.cpp)

    扫描二维码关注公众号,回复: 12476590 查看本文章
  • void ObjectSynchronizer::fast_enter(Handle obj, BasicLock* lock, bool attempt_rebias, TRAPS) {
     if (UseBiasedLocking) {
        if (!SafepointSynchronize::is_at_safepoint()) {
            // 为什么要revoke--撤销偏向锁
            // 因为匿名偏向可能是偏向别人
          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 (obj, lock, THREAD) ;
    }
    
  • 要么进入slow_enter

  • // -----------------------------------------------------------------------------
    // Interpreter/Compiler Slow Case
    // This routine is used to handle interpreter/compiler slow case
    // We don't need to use fast path here, because it must have been
    // failed in the interpreter/compiler code.
    // 轻量锁加锁
    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()) {
        // Anticipate successful CAS -- the ST of the displaced mark must
        // be visible <= the ST performed by the CAS.
        lock->set_displaced_header(mark);
          // 判断mark里面是不是00000001,如果是则cas成功,把mark word更新为lock reocrd的指针地址
        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;
      }
    
    #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
    
      // The object header will never be displaced to this lock,
      // so it does not matter what the value is, except that it
      // must be non-zero to avoid looking like a re-entrant lock,
      // and must not look locked either.
      lock->set_displaced_header(markOopDesc::unused_mark());
        // 膨胀
      ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD);
    }
    

底层创建lock record命令

  • entry->set_obj(lockee);创建一个lock record,并指向对象

场景:第二个线程释放锁后,第三个线程尝试获取锁

4.1 t2指第二个线程进来,并释放后,对象头里面存的是000001,因为t2是轻量锁,释放之后对象头是无锁

4.2 t2把锁释放了,线程私有栈里面是空的,t3进入加锁,t3 上面偏向的判断逻辑全部失败,直接进入if(!success)代码区域,会在内存中生成一个无锁的001对象头,把私有栈里面的header改为新的cpu里面的对象头,同时把加锁对象里面对象头的值替换成entry(锁记录,里面存的是指针),就是线程私有栈里面的指针,只占62位,最后两位就是00。

偏向锁的对象头组成

5.偏向锁已经偏向101

线程id+epo+ud+age+bl+lock

epo是一个时间标识

对于同一个线程,第一次偏向和第二次偏向哪个效率高—偏向锁性能高就高在这里

  • 肯定是第二次,只用判断是不是同一个线程,如果是,就直接成功了,
  • 如果是第一次,还有匿名偏向加锁的流程要判断

匿名偏向什么时候会失败

  • t1和t2同时来第一次加锁,有一个必定会失败,因为此时mark word中的值已经被修改成加锁成功的线程id+101,另外一个线程必然会失败

判断匿名偏向锁释放过期

  • // 是否过期
    else if ((anticipated_bias_locking_value & epoch_mask_in_place) !=0) {
        // try rebias
        markOop new_header = (markOop) ( (intptr_t) lockee->klass()->prototype_header() | thread_ident);
        if (hash != markOopDesc::no_hash) {
            new_header = new_header->copy_set_hash(hash);
        }
        if (lockee->cas_set_mark(new_header, mark) == mark) {
            if (PrintBiasedLockingStatistics)
                (* BiasedLocking::rebiased_lock_entry_count_addr())++;
        }
        else {
            CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
        }
        success = true;
    }
    

批量重偏向的优化

  • public static void main(String[] args) throws InterruptedException {
          
          
        //101  可偏向 没有线程偏向
    
        //a 没有线程偏向---匿名    101偏向锁
        List<A> list = new ArrayList<>();
    
        t1 = new Thread(){
          
          
            @Override
            public void run() {
          
          
                for(int i=0;i<19;i++){
          
          
                    A a = new A();
                    list.add(a);
                    synchronized (a){
          
          
                        log.debug(i+" "+ClassLayout.parseInstance(a).toPrintableTest(a));
                    }
                }
    
                log.debug("=========================");
                LockSupport.unpark(t2);
            }
    
    
        };
    
  • 这里打印的20次,结果应该都是偏向锁

  • 偏向锁过期涉及到批量撤销,当偏向锁撤销到一定次数,就会引发批量重偏向

  • public static void main(String[] args) throws InterruptedException {
          
          
        //101  可偏向 没有线程偏向
    
        //a 没有线程偏向---匿名    101偏向锁
        List<A> list = new ArrayList<>();
    
        t2 = new Thread(){
          
          
            @Override
            public void run() {
          
          
                for(int i=0;i<loopFlag;i++){
          
          
                    // 19不同的A对象
                    A a = new A();
                    list.add(a);
                    synchronized (a){
          
          
                        log.debug(i+" "+ClassLayout.parseInstance(a).toPrintableTest(a));
                    }
                }
    
                log.debug("=========================");
                // 叫醒t3
                LockSupport.unpark(t3=);
            }
    
    
        };
    
        t3 = new Thread(){
          
          
            @Override
            public void run() {
          
          
                // 等待t2执行完了叫醒再执行
                LockSupport.park();
                for (int i = 0; i <19 ; i++) {
          
          
                    A a = list.get(i);
                    log.debug(i+" "+ClassLayout.parseInstance(a).toPrintableTest(a));
                    synchronized (a){
          
          
                        log.debug(i+" "+ClassLayout.parseInstance(a).toPrintableTest(a));
                    }
                    log.debug(i+" "+ClassLayout.parseInstance(a).toPrintableTest(a));
                }
            }
        };
    
        t2.start();
        t3.start();
    }
    
  • t3中首先是偏向锁00000101,然后撤销升级为轻量锁11110000,最后释放锁变为无锁00000001

  • 但是发生了大量的偏向锁撤销,注意对同一个类的偏向锁撤销超过20次后,将上面的20改成30,会发生什么?

    • jvm中如果对A这个类进行了超过20次偏向锁撤销,会进行批量重偏向
    • 当超过20次后,所以A类的对象全都偏向给t3,三次都00000101
    • 这种表现出的结果就像是锁降级
  • public static void main(String[] args) throws InterruptedException {
        //101  可偏向 没有线程偏向
    
        //a 没有线程偏向---匿名    101偏向锁
        List<A> list = new ArrayList<>();
    
        t2 = new Thread(){
            @Override
            public void run() {
                for(int i=0;i<loopFlag;i++){
                    // 19不同的A对象
                    A a = new A();
                    list.add(a);
                    synchronized (a){
                        log.debug(i+" "+ClassLayout.parseInstance(a).toPrintableTest(a));
                    }
                }
    
                log.debug("=========================");
                // 叫醒t3
                LockSupport.unpark(t3=);
            }
    
    
        };
    
        t3 = new Thread(){
            @Override
            public void run() {
                // 等待t2执行完了叫醒再执行
                LockSupport.park();
                for (int i = 0; i <19 ; i++) {
                    A a = list.get(i);
                    log.debug(i+" "+ClassLayout.parseInstance(a).toPrintableTest(a));
                    synchronized (a){
                        log.debug(i+" "+ClassLayout.parseInstance(a).toPrintableTest(a));
                    }
                    log.debug(i+" "+ClassLayout.parseInstance(a).toPrintableTest(a));
                }
            }
        };
    
        t2.start();
        t3.start();
    	t3.join();
        log.debug(ClassLayout.parseInstance(new A()).toPrintable());
    }
    
  • 最后打印的new A(),是00000101,已经是批量重偏向的结果

  • 但是如果把for循环的次数改成10次,打印的结果,也是00000101,但是这是默认的无锁可偏向的结果

  • static  Thread t2;
    static Thread t3;
    static Thread t4;
    static int loopFlag=38;
    public static void main(String[] args) throws InterruptedException {
          
          
        //101  可偏向 没有线程偏向
    
        //a 没有线程偏向---匿名    101偏向锁
        List<A> list = new ArrayList<>();
    
        t2 = new Thread(){
          
          
            @Override
            public void run() {
          
          
                for(int i=0;i<50;i++){
          
          
                    A a = new A();
                    list.add(a);
                    synchronized (a){
          
          
                        log.debug(i+" "+ClassLayout.parseInstance(a).toPrintableTest(a));
                    }
                }
    
                log.debug("=========================");
                LockSupport.unpark(t3);
            }
    
    
        };
    
    
        t3 = new Thread(){
          
          
    
            @Override
            public void run() {
          
          
                LockSupport.park();
                for (int i = 0; i <50 ; i++) {
          
          
                    A a = list.get(i);
                    log.debug(i+" "+ClassLayout.parseInstance(a).toPrintableTest(a));
                    synchronized (a){
          
          
                        log.debug(i+" "+ClassLayout.parseInstance(a).toPrintableTest(a));
                    }
                    log.debug(i+" "+ClassLayout.parseInstance(a).toPrintableTest(a));
                }
    
                log.debug("======t3=====================================");
                LockSupport.unpark(t4);
    
            }
        };
    
    
    
    
        t4 = new Thread(){
          
          
            @Override
            public void run() {
          
          
                LockSupport.park();
                for (int i = 0; i <50 ; i++) {
          
          
                    A a = list.get(i);
                    log.debug(i+" "+ClassLayout.parseInstance(a).toPrintableTest(a));
                    synchronized (a){
          
          
                        log.debug(i+" "+ClassLayout.parseInstance(a).toPrintableTest(a));
                    }
                    log.debug(i+" "+ClassLayout.parseInstance(a).toPrintableTest(a));
                }
            }
        };
    
        t2.start();
        t3.start();
        t4.start();
        t4.join();
    
        log.debug(ClassLayout.parseInstance(new A()).toPrintable());
    }
    
  • 如果对某个类的偏向锁撤销达到40次,此时执行最后的log.debu打印出的结果是00000001

  • 这是jvm中的又一个阈值,如果对同一个类偏向撤销超过40次,此时会禁用偏向锁,也就是无锁不可偏向状态

总结

  • t1来加锁,50次加锁 类的不同对象,有50个A,全都是偏向锁,偏向t1
  • t2来加锁,前20个是轻量锁,也就是进行了20次偏向锁撤销(先是偏向锁,然后撤销偏向锁,再变成轻量锁),剩下30个全部都是偏向锁(批量重偏向),偏向t2,此时总共进行了20次偏向锁撤销
  • t3来加锁,前20个直接是轻量锁(因为上一步轻量锁撤销后是001,即不可偏向,是直接变成轻量锁),所以没有偏向锁撤销,剩下的30个都是偏向t2,又是轻量锁撤销,当撤销达到40次后,剩下的10个就不会一个一个的撤销了,会禁用偏向锁,不管这个类的任何对象都是不可偏向的

在类当中存有对象头的数据—偏向锁过期问题

  • class类模板存在元空间中,对象头的数据存在类模板中,对应于偏向锁,其中有一个epo值(两位)

  • epo值是一个类被装载到元空间中就会存在了,如果发生批量重偏向后,epo值就会加一

  • // 是否过期 如果过期则需要重偏向--- t2 第20次加锁
    else if ((anticipated_bias_locking_value & epoch_mask_in_place) !=0) {
        // try rebias 
        // 生成一个偏向当前线程的mark word
        markOop new_header = (markOop) ( (intptr_t) lockee->klass()->prototype_header() | thread_ident);
        if (hash != markOopDesc::no_hash) {
            new_header = new_header->copy_set_hash(hash);
        }
        // 就是对象头被别人改了
        // mark是这个线程记录的,如果没被别人改,肯定是可以成功的
        if (lockee->cas_set_mark(new_header, mark) == mark) {
            if (PrintBiasedLockingStatistics)
                (* BiasedLocking::rebiased_lock_entry_count_addr())++;
        }
        else {
            CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
        }
        success = true;
    }
    

轻量锁的重入—000

  • synchronized(this){
          
          
    
    	synchronized(this)
    }
    
  • // traditional lightweight locking
    if (!success) {
        markOop displaced = lockee->mark()->set_unlocked();
        entry->lock()->set_displaced_header(displaced);
        bool call_vm = UseHeavyMonitors;
        if (call_vm || lockee->cas_set_mark((markOop)entry, displaced) != displaced) {
            // Is it simple recursive case?
            // 轻量锁的重入
            if (!call_vm && THREAD->is_lock_owned((address) displaced->clear_lock_bits())) {
                // 在当前线程的私有线程栈里创造一条null的lock record
                entry->lock()->set_displaced_header(NULL);
            } else {
                CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
            }
        }
    }
    UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
    } else {
        istate->set_msg(more_monitors);
        UPDATE_PC_AND_RETURN(0); // Re-execute
    }
    }
    

猜你喜欢

转载自blog.csdn.net/Markland_l/article/details/113533717