java 偏向锁的撤销_源码解析-偏向锁撤销流程解读
一、单个偏向锁的撤销
static BiasedLocking::Condition revoke_bias(oop obj, bool allow_rebias, bool is_bulk, JavaThread* requesting_thread) {
markOop mark = obj->mark();
// 如果对象不是偏向锁,直接返回 NOT_BIASED
if (!mark->has_bias_pattern()) {
...
return BiasedLocking::NOT_BIASED;
}
uint age = mark->age();
// 构建两个 mark word,一个是匿名偏向模式(101),一个是无锁模式(001)
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) {
// 匿名偏向。当调用锁对象原始的 hashcode() 方法会走到这个逻辑
// 如果不允许重偏向,则将对象的 mark word 设置为无锁模式
if (!allow_rebias) {
obj->set_mark(unbiased_prototype);
}
...
return BiasedLocking::BIAS_REVOKED;
}
// 判断偏向线程是否还存活
bool thread_is_alive = false;
// 如果当前线程就是偏向线程
if (requesting_thread == biased_thread) {
thread_is_alive = true;
} else {
// 遍历当前 jvm 的所有线程,如果能找到,则说明偏向的线程还存活
for (JavaThread* cur_thread = Threads::first(); cur_thread != NULL; cur_thread = cur_thread->next()) {
if (cur_thread == biased_thread) {
thread_is_alive = true;
break;
}
}
}
// 如果偏向的线程已经不存活了
if (!thread_is_alive) {
// 如果允许重偏向,则将对象 mark word 设置为匿名偏向状态,否则设置为无锁状态
if (allow_rebias) {
obj->set_mark(biased_prototype);
} else {
obj->set_mark(unbiased_prototype);
}
...
return BiasedLocking::BIAS_REVOKED;
}
// 线程还存活则遍历线程栈中所有的 lock record
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);
// 如果能找到对应的 lock record,说明偏向所有者正在持有锁
if (mon_info->owner() == obj) {
...
// 升级为轻量级锁,修改栈中所有关联该锁的 lock record
// 先处理所有锁重入的情况,轻量级锁的 displaced mark word 为 NULL,表示锁重入
markOop mark = markOopDesc::encode((BasicLock*) NULL);
highest_lock = mon_info->lock();
highest_lock->set_displaced_header(mark);
} else {
...
}
}
if (highest_lock != NULL) { // highest_lock 如果非空,则它是最早关联该锁的 lock record
// 这个 lock record 是线程彻底退出该锁的最后一个 lock record
// 所以要,设置 lock record 的 displaced mark word 为无锁状态的 mark word
// 并让锁对象的 mark word 指向当前 lock record
highest_lock->set_displaced_header(unbiased_prototype);
obj->release_set_mark(markOopDesc::encode(highest_lock));
...
} else {
// 走到这里说明偏向所有者没有正在持有锁
...
if (allow_rebias) {
// 设置为匿名偏向状态
obj->set_mark(biased_prototype);
} else {
// 将 mark word 设置为无锁状态
obj->set_mark(unbiased_prototype);
}
}
return BiasedLocking::BIAS_REVOKED;
}
简单总结下偏向撤销的流程:
偏向所有者没有正在持有待撤销的偏向锁,如果允许重偏向则偏向锁变可偏向锁;否则偏向锁撤销为无锁。
偏向所有者正在持有该偏向锁,撤销该锁为偏向所有者正在持有的轻量级锁。
细节补充:
如何判断偏向所有者没有正在持有该偏向锁?
分两步,首先判断偏向所有者是否还活着,如果还活着,则遍历它的栈,看是否能找到关联该锁的锁记录,如果找到,则正在持有,如果没找到,则没有持有。(遍历过程在一个安全点执行,此时偏向所有者被阻塞。)
偏向所有者正在持有该偏向锁,如何将其撤销为轻量级锁?
遍历偏向所有者的栈,修改与该锁关联的所有锁记录,让偏向所有者以为它对该对象加的就是轻量级锁。
源码中的 highest_lock,为什么说是最早关联偏向锁的锁记录呢?
首先,锁记录在栈里是连续存放的。
请求获取锁时,按照从低地址到高地址的顺序,找在已关联该锁的锁记录之前,最后一个空闲的锁记录(没有指向任何锁对象)。
请求锁的源码如下:
CASE(_monitorenter): {
// lockee 就是锁对象
oop lockee = STACK_OBJECT(-1);
// derefing's lockee ought to provoke implicit null check
CHECK_NULL(lockee);
// 如果 lock record 列表里,已存在关联该锁的 lock record
// 则找在其之前的最后一个空闲 lock record
// 否则找最后一个空闲 lock record
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;
// 碰到已存在关联该锁的 lock record,直接退出
else if (most_recent->obj() == lockee) break;
most_recent++;
}
// entry 不为 NULL,代表还有空闲的 lock record
if (entry != NULL) {
...
} else {
// lock record 不够,重新执行
istate->set_msg(more_monitors);
UPDATE_PC_AND_RETURN(0); // Re-execute
}
}
而撤销偏向锁时,遍历偏向所有者的锁记录,也是按照从低地址到高地址的顺序,但它没有 break 的逻辑,因为它要处理所有关联该锁的锁记录。所以退出循环后,highest_lock 指向的是最早关联该锁的锁记录。
二、批量撤销
包含批量撤销逻辑的源码:
// 本来只要撤销 o 这一个对象的偏向锁
// 但是在这次单个对象的偏向撤销中,触发了批量重偏向或批量撤销
static BiasedLocking::Condition bulk_revoke_or_rebias_at_safepoint(oop o,
bool bulk_rebias,
bool attempt_rebias_of_object,
JavaThread* requesting_thread) {
...
jlong cur_time = os::javaTimeMillis();
o->klass()->set_last_biased_lock_bulk_revocation_time(cur_time);
Klass* k_o = o->klass();
Klass* klass = k_o;
if (bulk_rebias) {
// 批量重偏向的逻辑
...
} else {
...
// 批量撤销的逻辑
// 首先,禁用 类元数据 里的可偏向属性
// markOopDesc::prototype() 返回的是一个关闭偏向模式的 prototype
klass->set_prototype_header(markOopDesc::prototype());
// 其次,遍历所有线程的栈,撤销该类正在被持有的偏向锁为轻量级锁
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()) {
// 具体撤销,还是通过调用之前说的 revoke_bias() 方法做的
revoke_bias(owner, false, true, requesting_thread);
}
}
}
// 当前锁对象可能未被任何线程持有
// 所以这里单独进行撤销,以确保完成调用方的撤销语义
revoke_bias(o, false, true, requesting_thread);
}
...
}
禁用类的可偏向属性有两点作用:
该类新分配的对象都是 无锁 状态。
后续,线程请求该类的偏向锁对象时,发现类的可偏向属性被禁用,则直接加轻量级锁。(针对的是批量撤销时,未被线程正在持有的偏向锁。)
对于批量撤销时,正在被线程持有的偏向锁,通过在安全点遍历所有 Java 线程的栈,将偏向锁撤销为轻量级锁。
java 偏向锁的撤销_源码解析-偏向锁撤销流程解读相关推荐
- Redis进阶- Redisson分布式锁实现原理及源码解析
文章目录 Pre 用法 Redisson分布式锁实现原理 Redisson分布式锁源码分析 redisson.getLock(lockKey) 的逻辑 redissonLock.lock()的逻辑 r ...
- Java集合框架之三:HashMap源码解析
Java集合框架之三:HashMap源码解析 版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! HashMap在我们的工作中应用的非常广泛,在工作面试中也经常会被问到,对于这样一个重要的集 ...
- 源码解析-偏向锁撤销流程解读
一.单个偏向锁的撤销 源码链接:http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/9ce27f0a4683/src/share/vm/runtim ...
- 【Java】HashMap的数据结构、源码解析 - 公开课笔记
主要内容 Hashmap的数据结构 HashMap实现原理 HashMap源码解析 HashMap底层的数据结构? 1.7之前:数组+链表 1.8之后:数组+链表+红黑树 bucket 1.7之前: ...
- 详细讲解go web框架之gin框架源码解析记录及思路流程和理解
开篇 首先gin 框架是在 官方提供的net/http标准包进行的相应封装. 那么要想理解gin框架, 就要先懂一些 net/http标准包 的相关知识. 可以参考中文的 文档: https://st ...
- Glide 4.9源码解析-图片加载流程
本文Glide源码基于4.9,版本下载地址如下:Glide 4.9 前言 由于Glide源码真的很复杂,因此本文只分析和贴出与图片加载流程相关的功能以及代码.另外本文Glide源码基于4.9,与3.x ...
- java lock可重入_Java源码解析之可重入锁ReentrantLock
本文基于jdk1.8进行分析. ReentrantLock是一个可重入锁,在ConcurrentHashMap中使用了ReentrantLock. 首先看一下源码中对ReentrantLock的介绍. ...
- java商品信息管理系统代码_[源码分享]学生信息管理系统(管理员)
我一直以为学生信息管理系统是烂大街的,网上一搜一大把的那种 毕竟这种项目是学完C语言之后都可以独立完成的项目,只有界面好看与否的问题 最近好多学生问学生信息管理系统的代码,估计是C语言大作业什么的.然 ...
- Java并发编程之AQS以及源码解析
文章目录 概览 实现思路 实现原理 源自CLH锁 AQS数据模型 CAS操作 主要方法 自定义同步器的实现方法 AQS定义的模板方法 源码解读 等待状态释义 AQS获取锁的流程图 获取独占锁的实现 总 ...
- faster rcnn接口_源码解析faster rcnn (mask rcnn)全过程
1. 总领过程--官方faster cnnn 调用过程 import torchvision, torch # 导入官方faster rcnn 模型 model = torchvision.model ...
最新文章
- iOS10.3 的评论系统
- css菜单缓慢滑动_如何使用HTML,CSS和JavaScript构建滑动菜单栏
- Android Paint应用之自定义View实现进度条控件
- LeetCode 5178. 四因数
- c 普通的文本变成注释文本的快捷键_phpstrom 快捷键,记一下记一下!(life)
- DotNetNuke(DNN)皮肤制作--如何修改DNN中众多的CSS文件
- 《防患未然:实施情报先导的信息安全方法与实践》——2.3 情报循环
- lzg_ad:使用EWF API开发常见问题
- 平面三角形外接圆圆心与半径求解算法
- 黑马java学习笔记4 强化部分 常见API
- o2o模式都有哪些应用场景?
- xmapp启动数据库问题记录 Access denied for user ‘me‘@‘localhost‘ (using password: NO)
- 2017初二上期中考试总结
- edup无线网卡驱动安装linux,EDUP EP-N8513 (RTL8188CUS芯片)在Ubuntu 12.10下的wifi不能连接问题解决方法...
- 【多数据中心】分布式数据同步设计方案
- Java中如何实现数组输入和输出
- NLP自然语言处理-机器学习和自然语言处理介绍(四)
- 爱奇艺《大魔术师》海内外备受关注
- 技术前沿与经典文章8:智能家居发展白皮书——全屋智能解决方案
- 微信小程序中使用slot插槽