【正本清源】Synchronized 源码全解之偏向锁的获取(基于 Openjdk12)


基于 hotspot 源码深度解析 Synchronized 偏向锁原理。欢迎友好交流,最好附带源码作为论据。

BasicObjectLock

包装了锁对象的头部和锁对象的引用。

class BasicObjectLock {
  friend class VMStructs;
 private:
  // 保存了被替换下来的对象的头部
  BasicLock _lock;                                    // the lock, must be double word aligned
  // oop java 对象的引用
  // typedef class oopDesc* oop;                            
  oop       _obj;                                     // object holds the lock;

 public:
  // Manipulation
  oop      obj() const                                { return _obj;  }
  void set_obj(oop obj)                               { _obj = obj; }
  BasicLock* lock()                                   { return &_lock; }

  static int size()                                   { return sizeof(BasicObjectLock)/wordSize; }

  void oops_do(OopClosure* f) { f->do_oop(&_obj); }

  static int obj_offset_in_bytes()                    { return offset_of(BasicObjectLock, _obj);  }
  static int lock_offset_in_bytes()                   { return offset_of(BasicObjectLock, _lock); }
};

BasicLock

class BasicLock {
  friend class VMStructs;
  friend class JVMCIVMStructs;
 private:
  volatile markOop _displaced_header; // 被替换下来的对象头部
 public:
  markOop      displaced_header() const               { return _displaced_header; }
  void         set_displaced_header(markOop header)   { _displaced_header = header; }

  void print_on(outputStream* st) const;

  void move_to(oop obj, BasicLock* dest);

  static int displaced_header_offset_in_bytes()       { return offset_of(BasicLock, _displaced_header); }
};

oopDesc

hotspot/src/share/vm/oops/oop.hpp
所有对象头部的基类

typedef class markOopDesc* markOop; // oopsHierarchy.hpp

class oopDesc {
  friend class VMStructs;
 private:
  volatile markOop  _mark; // 其实就是 markOopDesc
  union _metadata { // 指向对象所属 class 对象指针
    Klass*      _klass;
    narrowKlass _compressed_klass; // 开启指针压缩后,指向压缩后的 class 对象
  } _metadata;

markOopDesc

hotspot/src/share/vm/oops/markOop.hpp
描述了标记位,其实就是带标记的 oop 对象

class markOopDesc: public oopDesc {
 private:
  uintptr_t value() const { return (uintptr_t) this; }

 public:
  enum { age_bits                 = 4, // 对象年龄 4bit
         lock_bits                = 2, // 锁标记 2bit
         biased_lock_bits         = 1, // 偏向锁 1bit
         max_hash_bits            = BitsPerWord - age_bits - lock_bits - biased_lock_bits,
         hash_bits                = max_hash_bits > 31 ? 31 : max_hash_bits,
         cms_bits                 = LP64_ONLY(1) NOT_LP64(0),
         epoch_bits               = 2
  };
  enum { no_hash = 0 }; // no hash value assigned
  // ...
}

偏向锁

偏向锁的获取 monitorenter

  1. 偏向锁已经偏向当前线程:那么直接获取锁成功
  2. 对象类的原始头部偏向锁位发生改变,那么撤销偏向锁(将原始头部 CAS 到当前对象头部)
  3. 原始对象头部的 epoch 发生变化,此时表示批量偏向锁撤销,那么尝试重新获取偏向锁
  4. 匿名偏向锁,那么尝试获取偏向锁
  5. 上述失败后,那么需要进行锁升级
源码验证(bytecodeInterpreter.cpp)
      CASE(_monitorenter): {
        oop lockee = STACK_OBJECT(-1);
        // 每个 java 的线程栈上都有一组 BasicObjectLock 对象
        // 在线程栈上找到一个空闲的 BasicObjectLock 对象
        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) {
          // 保存锁对象,表明当前 BasicObjectLock 持有锁对象 lockee
          entry->set_obj(lockee); 
          int success = false;
          uintptr_t epoch_mask_in_place = (uintptr_t)markOopDesc::epoch_mask_in_place; // 获取 epoch(用于标识偏向锁的轮次),用于批量锁撤销。

          // 获取锁对象的头部标记信息
          markOop mark = lockee->mark();
          // enum { no_hash = 0 };
          intptr_t hash = (intptr_t) markOopDesc::no_hash;
          
          // ====== if (mark->has_bias_pattern()) 开始 ===
          
          // 判断是否开启了偏向锁。若开启偏向锁,锁状态位为 5
          // [JavaThread* | epoch | age | 1 | 01](src/hotspot/share/oops/markOop.hpp)
          if (mark->has_bias_pattern()) { 
            uintptr_t thread_ident;
            uintptr_t anticipated_bias_locking_value;
            thread_ident = (uintptr_t)istate->thread();
            // 比较原始对象与当前对象除了年龄位的其他位是否一致
            // lockee->klass()->prototype_header():对象的原始头部信息(类的信息),类是对象的模板,对象是类的实例
            // ((uintptr_t)lockee->klass()->prototype_header() | thread_ident):将对象的原始信息组合上线程标识
            // (((uintptr_t)lockee->klass()->prototype_header() | thread_ident) ^ (uintptr_t)mark):将对象的原始信息组合上线程标识的结果与当前对象头部比较,若完全相同则为  0,表示当前对象已经偏向了当前线程
            //  & ~((uintptr_t) markOopDesc::age_mask_in_place):避免由于年龄位不一致导致上一步结果不为 0
            anticipated_bias_locking_value =
              (((uintptr_t)lockee->klass()->prototype_header() | thread_ident) ^ (uintptr_t)mark) &
              ~((uintptr_t) markOopDesc::age_mask_in_place); 

            // 1. 原始对象与当前对象除了年龄位的其他位一致
            // 表明还没有批量撤销偏向锁,且当前线程持有了偏向锁,直接退出
            if  (anticipated_bias_locking_value == 0) { 
              // already biased towards this thread, nothing to do
              if (PrintBiasedLockingStatistics) {
                (* BiasedLocking::biased_lock_entry_count_addr())++;
              }
              success = true; // 当前线程已经获取了偏向锁,直接退出
            }
            
            // 执行到此处说明 anticipated_bias_locking_value 不为 0
            
            // 2. 从 anticipated_bias_locking_value 值中截断出偏向锁位,若存在差异,说明原始对象头部的偏向锁位发生改变,此时撤销对象的偏向锁(注意:一定是原始对象头部发生改变,因为 if (mark->has_bias_pattern()) 前置判断已经定义当前对象的头部偏向锁存在)
			// markOopDesc::biased_lock_mask_in_place) 偏向锁的位
            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);
              }
              // CAS 将对象头从 mark 替换为 header 撤销偏向锁
              if (lockee->cas_set_mark(header, mark) == mark) {
                if (PrintBiasedLockingStatistics)
                  (*BiasedLocking::revoked_lock_entry_count_addr())++;
              }
            }

			// 执行到此处说明 (anticipated_bias_locking_value & markOopDesc::biased_lock_mask_in_place) == 0

            // 3. 在批量撤销偏向锁的时候需要更改 epoch 的值
            // 此处 epoch 不为 0,说明偏向锁已被批量撤销,此处需要进行重偏向 
            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);
              }
              // CAS 重偏向
              if (lockee->cas_set_mark(new_header, mark) == mark) {
                if (PrintBiasedLockingStatistics)
                  (* BiasedLocking::rebiased_lock_entry_count_addr())++;
              }
              else {
                // CAS 失败,发生了竞争,需要进入 monitorenter
                CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
              }
              success = true;
            }

            // 4. 以上条件均不满足,说明开启了偏向锁,此时偏向锁的状态为匿名偏向锁,尝试 CAS 将其偏向为当前线程
            // 匿名重偏向(偏向锁机制已经打开,但是还未被其他线程偏向)
            else { 
              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);
              // CAS 重偏向
              if (lockee->cas_set_mark(new_header, header) == header) {
                if (PrintBiasedLockingStatistics)
                  (* BiasedLocking::anonymously_biased_lock_entry_count_addr())++;
              }
              else {
                // CAS 失败,发生了竞争,进入 monitorenter
                CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
              }
              success = true;
            }
          }

          // ====== if (mark->has_bias_pattern()) 结束 ===
          
          // traditional lightweight locking
          // 偏向锁获取失败,进入传统的轻量级锁
          // ====== if (!success) 开始 ===
          
          if (!success) { 
            // 构造一个无锁状态的对象头部(锁标志位:01)
            markOop displaced = lockee->mark()->set_unlocked(); 
            // 将上述构造的对象头部保存到当前线程的 BasicObjectLock 容器中
            // BasicObjectLock* entry
            entry->lock()->set_displaced_header(displaced); 
            
            // UseHeavyMonitors 若为 true,则直接使用重量级锁
            // call_vm 默认为 false,忽略即可。
            bool call_vm = UseHeavyMonitors; 
            
            // 尝试获取轻量级锁:
            // 将对象头部原始信息保存在线程栈的 BasicObjectLock 容器中
            // CAS 将对象头部替换为 BasicObjectLock 的地址,若替换成功表示上锁成功
            // 替换值 entry,期望值 displaced,替换成功返回期望值 displaced(displaced 为无锁对象)
            // cas_set_mark(markOop new_mark, markOop old_mark)
            // Atomic::cmpxchg_ptr(new_mark, &_mark, old_mark)
            if (lockee->cas_set_mark((markOop)entry, displaced) != displaced) { 
              // 如果失败,可能是当前线程轻量级锁重入
              // 判断是否是锁重入,通过 is_lock_owned 方法查看对象头部的轻量级锁的 BasicObjectLock 是否属于当前线程,若是,则为轻量级锁重入。
              if (THREAD->is_lock_owned((address) displaced->clear_lock_bits())) {
                // 轻量级锁重入:由于 BasicObjectLock 是一个栈,所以只需要在栈底的 BasicObjectLock 保存替换头部即可,其他的 BasicObjectLock 不需要保存。因为只有最后一个重入锁释放才会替换掉头部。
                entry->lock()->set_displaced_header(NULL);
              } else { // 不是锁重入
                // 偏向锁和轻量级锁获取失败,进入 monitorenter,进行锁升级
                CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception); 
              }
            }
          }
          // ====== if (!success) 结束 ===
          
          UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
        } else {
          // 如果没有找到空闲的 BasicObjectLock,则设置 more_monitors 标志位,由解释器分配新的 BasicObjectLock 并重试
          istate->set_msg(more_monitors);
          UPDATE_PC_AND_RETURN(0); // Re-execute
        }
      }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值