文章目录

  • BasicObjectLock
  • BasicLock
  • oopDesc
  • markOopDesc
  • 偏向锁
    • 偏向锁的获取 monitorenter
      • 源码验证(bytecodeInterpreter.cpp)

基于 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:// Manipulationoop      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.hppclass oopDesc {friend class VMStructs;private:volatile markOop  _mark; // 其实就是 markOopDescunion _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, // 对象年龄 4bitlock_bits                = 2, // 锁标记 2bitbiased_lock_bits         = 1, // 偏向锁 1bitmax_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 持有锁对象 lockeeentry->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):避免由于年龄位不一致导致上一步结果不为 0anticipated_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 doif (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 rebiasmarkOop 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 失败,发生了竞争,需要进入 monitorenterCALL_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 失败,发生了竞争,进入 monitorenterCALL_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* entryentry->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}}

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

  1. php授权验证系统源码-全解开源版

    简介: php授权验证系统源码全解开源版,正版授权查询管理. 安装方法:上传PHP环境,访问域名instll,根据提示自动安装! 网盘下载地址: http://kekewl.org/MBJa7XXNk ...

  2. 最新超好看Nteam官网程序源码+全解移除授权

    正文: NanGe Nteam该项目适用于团队/工作室等类型,全站由layui强力驱动及后台模板的使用,源码测试过了,没啥问题. 包含功能: 团队介绍,项目展示,成员列表等 成员申请.作品申请.成员真 ...

  3. 吃鸡个人发卡源码全解无后门(游戏辅助点卡适用)

    介绍: 最新吃鸡个人发卡系统6.0源码 修复安装问题,新增后台自定义主页图片功能 后台更新ui,仿swap用户中心 安装说明:上传后访问即可根据提示安装 订单发送邮件监控域名/ayangw/mon.p ...

  4. c#获取网页源码全解

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  5. 2023新版微信九块九进群系统源码+全解的

    正文: 后台内容页面可以任意修改,不仅仅是表情包,还有壁纸,PPT,ps教程等一系列. 程序: wwiefu.lanzoul.com/iaAwo0fz9j8h 图片:

  6. 微宝自动更新影视源码全解

    介绍: 直接上传无需安装 后台/admin,账户admin,密码123456 网盘下载地址: http://kekewl.net/nBjFKKQCXuT 图片:

  7. cocos android-1,Cocos2D-Android-1之源码详解:5.Box2dTest

    Cocos2D-Android-1之源码详解:5.Box2dTest 发布时间:2020-08-06 06:19:28 来源:51CTO 阅读:398 作者:abab99 package org.co ...

  8. 机器学习算法源码全解析(三)-范数规则化之核范数与规则项参数选择

    前言 参见上一篇博文,我们聊到了L0,L1和L2范数,这篇我们絮叨絮叨下核范数和规则项参数选择.知识有限,以下都是我一些浅显的看法,如果理解存在错误,希望大家不吝指正.谢谢. 机器学习算法源码全解析( ...

  9. Tensorflow 2.x(keras)源码详解之第四章:DatasetTFRecord

      大家好,我是爱编程的喵喵.双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中.从事机器学习以及相关的前后端开发工作.曾在阿里云.科大讯飞.CCF等比赛获得多次Top名次.现 ...

最新文章

  1. crontab 最小间隔_今天我间隔了:如何找到不在数组中的最小数字
  2. 嵌入式linux root免密码,给嵌入式linux串口添加密码的一些总结
  3. 一个古帝国做产品的故事
  4. html5 建筑物模型,基于HTML5的建筑物阴影实时模拟
  5. 拦截Activity的后退键处理
  6. Excel 一键上传到数据库
  7. python 怎么调用 矩阵 第几行_python工厂第19层 多重列表1
  8. HarmonyOS 编译系统源码
  9. 吉大c 语言程序设计奥鹏作业,吉大18秋学期《C语言程序设计》在线作业一答案...
  10. 利用pandas处理二级office的Excel试题(一)
  11. 崩坏3mmd中的渲染技术研究
  12. 【狂神说笔记—— Java基础17-JavaScript,jQuery】
  13. 扫码点餐有哪些优势?
  14. centos7 安装/卸载wps 无法启动 字体缺失(亲测有效)
  15. Hadoop的java程序报错Exception in thread main java.io.FileNotFoundException: File does not exis
  16. avr c语言编译器,AVR单片机C语言编译器-20210409071159.docx-原创力文档
  17. The accumulated size of entities is 50,000,001 that exceeded the 50,000,000 limit set by FEATUR
  18. 游戏服务器被ddos攻击怎么办,如何防御?
  19. 免费手机上网的方法,比GPRS网速快很多(好东西与大家分亨)
  20. 路由器发展编年史 发展篇

热门文章

  1. “百度首页人物”首期人物:《士兵突击》许三多
  2. 脑洞故事|万圣节的起源并非恶魔,而是M星系机器人!
  3. VIM实用指南(3)复制,粘贴 ,删除,撤销,重做指令速记
  4. IntellJ IDEA 基础之 常用快捷键的简单整理
  5. Android Bluetooth HID Device模拟蓝牙键盘鼠标
  6. stg分区边界值问题
  7. 如何建立免费企业邮箱(腾讯、网易)
  8. 用友推出电子发票服务平台 互联网发力
  9. c语言提供的合法关键字,c语言提供的合法的数据类型关键字是什么
  10. 轻松注册WinRAR的小方法