资讯专栏INFORMATION COLUMN

从wait的源码看撤销偏向锁的过程(revoke and rebias)

HmyBmny / 3078人阅读

摘要:源码实现如下表示是否有异常如果是使用了偏向锁,要撤销偏向锁在中可以看到方法的实现。

wait源码实现如下

//TRAPS表示是否有异常
void ObjectSynchronizer::wait(Handle obj, jlong millis, TRAPS) {
  if (UseBiasedLocking) {
    //如果是使用了偏向锁,要撤销偏向锁
    BiasedLocking::revoke_and_rebias(obj, false, THREAD);
    assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now");
  }
 ...

biasedLocking.cpp中可以看到方法的实现。整体结构划分如下

BiasedLocking::Condition BiasedLocking::revoke_and_rebias(Handle obj, bool attempt_rebias, TRAPS) {
//1:必须在安全点
assert(!SafepointSynchronize::is_at_safepoint(), "must not be called while at safepoint");
//2:读取对象头
markOop mark = obj->mark();
if (mark->is_biased_anonymously() && !attempt_rebias) {
//3:没有线程获取了偏向锁
} else if (mark->has_bias_pattern()) {
//4:已经偏向了
}
//5:没有执行偏向,通过启发式的方式决定到底是执行撤销还是执行rebias
HeuristicsResult heuristics = update_heuristics(obj(), attempt_rebias);
if (heuristics == HR_NOT_BIASED) {
//5.1:偏向状态改成了不需要偏向
} else if (heuristics == HR_SINGLE_REVOKE) {
//5.2:启发式决定执行单次的撤销
}
//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);
return bulk_revoke.status_code();
}
没有获取偏向锁

这里表示还没有被偏向,并且不是执行rebias

// We are probably trying to revoke the bias of this object due to
// an identity hash code computation. Try to revoke the bias
// without a safepoint. This is possible if we can successfully
// compare-and-exchange an unbiased header into the mark word of
// the object, meaning that no other thread has raced to acquire
// the bias of the object.
markOop biased_value       = mark;
//prootype本身构建的是 markOop( no_hash_in_place | no_lock_in_place );
markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age());
//执行CAS,如果当前对象的mark没有变更,就换成 unbiased_prototype
markOop res_mark = (markOop) Atomic::cmpxchg_ptr(unbiased_prototype, obj->mark_addr(), mark);
if (res_mark == biased_value) {
  //如果之前的和现在的一样,说明撤销成功,BIAS_REVOKED本身是一个枚举  
return BIAS_REVOKED;
}
已经被其它线程获取了偏向
//已经被线程偏向了,获取Klass对象,即类本身的头,obj则是它的实例 
Klass* k = Klass::cast(obj->klass());
 markOop prototype_header = k->prototype_header();
 if (!prototype_header->has_bias_pattern()) {
    //对象当前的偏向状态已经过期,并且是不可偏向的,直接设置成已经撤销偏向即可  
 // This object has a stale bias from before the bulk revocation   
// for this data type occurred. It"s pointless to update the 
  // heuristics at this point so simply update the header with a   
// CAS. If we fail this race, the object"s bias has been revoked   
// by another thread so we simply return and let the caller deal   
// with it.   
  markOop biased_value       = mark;
   markOop res_mark = (markOop) Atomic::cmpxchg_ptr(prototype_header, obj->mark_addr(), mark);
   assert(!(*(obj->mark_addr()))->has_bias_pattern(), "even if we raced, should still be revoked");
   return BIAS_REVOKED;
 } else if (prototype_header->bias_epoch() != mark->bias_epoch()) {
   //实例的epoch和类本身的epoch值不一样,说明它已经过期,也就是说这个对象当前处于未偏向但是可偏向的状态(rebiasable)   
// The epoch of this biasing has expired indicating that the  
 // object is effectively unbiased. Depending on whether we need   
// to rebias or revoke the bias of this object we can do it   
// efficiently enough with a CAS that we shouldn"t update the   
// heuristics. This is normally done in the assembly code but we  
 // can reach this point due to various points in the runtime   
// needing to revoke biases.  
if (attempt_rebias) {
    //执行rebias     wait希望直接撤销
        assert(THREAD->is_Java_thread(), "");
     markOop biased_value       = mark;
     markOop rebiased_prototype = markOopDesc::encode((JavaThread*) THREAD, mark->age(), prototype_header->bias_epoch());
     markOop res_mark = (markOop) Atomic::cmpxchg_ptr(rebiased_prototype, obj->mark_addr(), mark);
     if (res_mark == biased_value) {
     //当前线程抢到了这个对象的偏向       
        return BIAS_REVOKED_AND_REBIASED;
     }
   } else {
     markOop biased_value       = mark;
     markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age());
    //CAS撤销偏向锁
     markOop res_mark = (markOop) Atomic::cmpxchg_ptr(unbiased_prototype, obj->mark_addr(), mark);
     if (res_mark == biased_value) {
       //撤销了偏向       
    return BIAS_REVOKED;
     }
   }
 }
启发式策略
//启发式的方式决定要做那种操作static HeuristicsResult update_heuristics(oop o, bool allow_rebias) {
  markOop mark = o->mark();
  if (!mark->has_bias_pattern()) {
    //不可偏向直接返回    return HR_NOT_BIASED;
  }
  //控制撤销的次数  

// Heuristics to attempt to throttle the number of revocations. 
 // Stages:  // 1. Revoke the biases of all objects in the heap of this type, 
 //    but allow rebiasing of those objects if unlocked.  
// 2. Revoke the biases of all objects in the heap of this type 
 //    and don"t allow rebiasing of these objects. Disable 
 //    allocation of objects of that type with the bias bit set.  Klass* k = o->blueprint();
  jlong cur_time = os::javaTimeMillis();
  //获取上次执行bulk revication的时间  jlong last_bulk_revocation_time = k->last_biased_lock_bulk_revocation_time();
  //获取执行bulk revocation的次数  int revocation_count = k->biased_lock_revocation_count();
  //定义在globs.hpp,BiasedLockingBulkRebiasThreshold取值为20;BiasedLockingBulkRevokeThreshold取值为40,BiasedLockingDecayTime为25000毫秒  
if ((revocation_count >= BiasedLockingBulkRebiasThreshold) &&
      (revocation_count <  BiasedLockingBulkRevokeThreshold) &&
      (last_bulk_revocation_time != 0) &&
      (cur_time - last_bulk_revocation_time >= BiasedLockingDecayTime)) {
    // This is the first revocation we"ve seen in a while of an  
  // object of this type since the last time we performed a bulk   
 // rebiasing operation. The application is allocating objects in    
// bulk which are biased toward a thread and then handing them    
// off to another thread. We can cope with this allocation  
  // pattern via the bulk rebiasing mechanism so we reset the  
  // klass"s revocation count rather than allow it to increase    
// monotonically. If we see the need to perform another bulk   
 // rebias operation later, we will, and if subsequently we see    
// many more revocation operations in a short period of time we   
 // will completely disable biasing for this type.   
 //在执行了一定时间之内,执行的撤销次数没有超过阈值,那么认为可以优先执行bulk rebias,因此将计数回归原始值    
    k->set_biased_lock_revocation_count(0);
    revocation_count = 0;
  }

  // Make revocation count saturate just beyond BiasedLockingBulkRevokeThreshold 
if (revocation_count <= BiasedLockingBulkRevokeThreshold) {
    //计算执行撤销的次数    

revocation_count = k->atomic_incr_biased_lock_revocation_count();
  }

  if (revocation_count == BiasedLockingBulkRevokeThreshold) {
    //达到执行bulk revoke的阈值,执行bulk revoke    return HR_BULK_REVOKE;
  }

  if (revocation_count == BiasedLockingBulkRebiasThreshold) {
    //达到 bulk rebias的阈值,执行bulk rebias   
return HR_BULK_REBIAS;
  }
  //默认执行单次的撤销  
return HR_SINGLE_REVOKE;
}
bulk_revoke_or_rebias_at_safepoint

bulk revoke的关键在于它会遍历所有线程栈的每一帧

static BiasedLocking::Condition bulk_revoke_or_rebias_at_safepoint(oop o,
                                                                   bool bulk_rebias,
                                                                   bool attempt_rebias_of_object,
                                                                   JavaThread* requesting_thread) {
…
if (bulk_rebias) {
     ...
  // Now walk all threads" stacks and adjust epochs of any biased  
// and locked objects of this data type we encounter 
//遍历所有的线程
for (JavaThread* thr = Threads::first(); thr != NULL; thr = thr->next()) {
  //遍历线程栈的每一帧,获取所有的监视器
    GrowableArray* cached_monitor_info = get_or_compute_monitor_info(thr);
    for (int i = 0; i < cached_monitor_info->length(); i++) {
      MonitorInfo* mon_info = cached_monitor_info->at(i);
      oop owner = mon_info->owner();
      markOop mark = owner->mark();
      if ((owner->klass() == k_o) && mark->has_bias_pattern()) {
        // We might have encountered this object already in the case of recursive locking        
        assert(mark->bias_epoch() == prev_epoch || mark->bias_epoch() == cur_epoch, "error in bias epoch adjustment");
        //更新所有栈中的有偏向锁的epoch        
        owner->set_mark(mark->set_bias_epoch(cur_epoch));
      }
    }
  }
...
// At this point we"re done. All we have to do is potentially// adjust the header of the given object to revoke its bias.
revoke_bias(o, attempt_rebias_of_object && klass->prototype_header()->has_bias_pattern(), true, requesting_thread);
}
…
if (attempt_rebias_of_object &&
    o->mark()->has_bias_pattern() &&
    klass->prototype_header()->has_bias_pattern()) {
 //bias_epoch本身则是表示获取了偏向锁
  markOop new_mark = markOopDesc::encode(requesting_thread, o->mark()->age(),
                                         klass->prototype_header()->bias_epoch());
  o->set_mark(new_mark);
//执行rebiase
  status_code = BiasedLocking::BIAS_REVOKED_AND_REBIASED;
 ...
}
}

revoke_bias的执行如下

static BiasedLocking::Condition revoke_bias(oop obj, bool allow_rebias, bool is_bulk, JavaThread* requesting_thread) {
markOop mark = obj->mark();
…
//偏向锁的头
markOop   biased_prototype = markOopDesc::biased_locking_prototype()->set_age(age);
//非偏向锁的头
markOop unbiased_prototype = markOopDesc::prototype()->set_age(age);
…
//获取偏向的线程
JavaThread* biased_thread = mark->biased_locker();
if (biased_thread == NULL) {
  // Object is anonymously biased. We can get here if, for 
 // example, we revoke the bias due to an identity hash code  
// being computed for an object.
  if (!allow_rebias) {
  //没有线程获取,又需要执行rebias,改掉对象头即可
    obj->set_mark(unbiased_prototype);
  }
 ...
  //撤销完毕  
  return BiasedLocking::BIAS_REVOKED;
}
…
//线程活着
//遍历栈帧,获取所有这个线程的监视器,按照最年轻到最老的顺序 
GrowableArray* cached_monitor_info = get_or_compute_monitor_info(biased_thread);
 BasicLock* highest_lock = NULL;
 for (int i = 0; i < cached_monitor_info->length(); i++) {
   MonitorInfo* mon_info = cached_monitor_info->at(i);
   if (mon_info->owner() == obj) {
   ...
     // Assume recursive case and fix up highest lock later    
 //当前栈帧存在了这个对象的锁     
    markOop mark = markOopDesc::encode((BasicLock*) NULL);
     highest_lock = mon_info->lock();
     //更新栈中的mark为NULL     
    highest_lock->set_displaced_header(mark);
   }
...
   }
 }
 if (highest_lock != NULL) {
   // Fix up highest lock to contain displaced header and point   
// object at it   
//将最久的那个lock更新为没有偏向  ,栈中  设置了锁记录 
highest_lock->set_displaced_header(unbiased_prototype);
   // Reset object header to point to displaced mark   
//将对象头指向栈中的位置,这样表示就没有偏向了
obj->set_mark(markOopDesc::encode(highest_lock));
   assert(!obj->mark()->has_bias_pattern(), "illegal mark state: stack lock used bias bit");
  ...
 } else {
  ...
   if (allow_rebias) {
     obj->set_mark(biased_prototype);
   } else {
     // Store the unlocked value into the object"s header.    
        obj->set_mark(unbiased_prototype);
   }
 }
//撤销完毕 return BiasedLocking::BIAS_REVOKED;
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/72752.html

相关文章

  • (六) synchronized源码分析

    摘要:关键字经过编译之后,会在同步块的前后分别形成和这两个字节码指令。当我们的把字节码加载到内存的时候,会对这两个指令进行解析。这两个字节码都需要一个类型的参数来指明要锁定和解锁的对象。最后唤醒暂停的线程。 文章简介 前面我有文章介绍了synchronized的基本原理,这篇文章我会从jvm源码分析synchronized的实现逻辑,希望让大家有一个更加深度的认识 内容导航 从synchr...

    jhhfft 评论0 收藏0
  • 偏向锁状态转移原理

    摘要:此时偏向拥有者会像轻量级锁操作那样,它的堆栈会填入锁记录,然后对象本身的会被更新成指向栈上最老的锁记录,然后线程本身在安全点的阻塞会被释放如果没有被原有的偏向锁持有者持有,会撤销对象重新回到可偏向但是还没有偏向的状态,然后尝试重新获取锁。 为什么需要偏向锁 当多个处理器同时处理的时候,通常需要处理互斥的问题。 一般的解决方式都会包含acquire和release这个两种操作,操作保证...

    alaege 评论0 收藏0
  • JAVA并发编程--2.synchronied实现原理

    摘要:实现原理虚拟机锁原理虚拟机中对象头部信息可以看见对象头中结构中的成员,允许压缩。否则,将偏向锁撤销,升级为轻量级锁。存在明显多线程竞争的场景下使用偏向锁是不合适的,例如生产者消费者队列。 synchronied实现原理 虚拟机锁原理 虚拟机中对象头部信息 /*hotspot/src/share/vm/oops/oop.hpp*/ class oopDesc { friend cla...

    mudiyouyou 评论0 收藏0
  • Java并发编程之旅总览

    摘要:线程安全的概念什么时候线程不安全怎样做到线程安全怎么扩展线程安全的类对线程安全的支持对线程安全支持有哪些中的线程池的使用与中线程池的生命周期与线程中断中的锁中常见死锁与活锁的实例线程同步机制显示锁使用与原理原理剖析原理中的与原理偏向锁状态 showImg(https://segmentfault.com/img/bVblUE9?w=1354&h=1660); 线程安全的概念 showI...

    Harpsichord1207 评论0 收藏0
  • (五)Synchronized原理分析

    摘要:而导致这个问题的原因是线程并行执行操作并不是原子的,存在线程安全问题。表示自旋锁,由于线程的阻塞和唤醒需要从用户态转为核心态,频繁的阻塞和唤醒对来说性能开销很大。 文章简介 synchronized想必大家都不陌生,用来解决线程安全问题的利器。同时也是Java高级程序员面试比较常见的面试题。这篇文正会带大家彻底了解synchronized的实现。 内容导航 什么时候需要用Synchr...

    greatwhole 评论0 收藏0

发表评论

0条评论

HmyBmny

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<