一、单个偏向锁的撤销

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 偏向锁的撤销_源码解析-偏向锁撤销流程解读相关推荐

  1. Redis进阶- Redisson分布式锁实现原理及源码解析

    文章目录 Pre 用法 Redisson分布式锁实现原理 Redisson分布式锁源码分析 redisson.getLock(lockKey) 的逻辑 redissonLock.lock()的逻辑 r ...

  2. Java集合框架之三:HashMap源码解析

    Java集合框架之三:HashMap源码解析 版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! HashMap在我们的工作中应用的非常广泛,在工作面试中也经常会被问到,对于这样一个重要的集 ...

  3. 源码解析-偏向锁撤销流程解读

    一.单个偏向锁的撤销 源码链接:http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/9ce27f0a4683/src/share/vm/runtim ...

  4. 【Java】HashMap的数据结构、源码解析 - 公开课笔记

    主要内容 Hashmap的数据结构 HashMap实现原理 HashMap源码解析 HashMap底层的数据结构? 1.7之前:数组+链表 1.8之后:数组+链表+红黑树 bucket 1.7之前: ...

  5. 详细讲解go web框架之gin框架源码解析记录及思路流程和理解

    开篇 首先gin 框架是在 官方提供的net/http标准包进行的相应封装. 那么要想理解gin框架, 就要先懂一些 net/http标准包 的相关知识. 可以参考中文的 文档: https://st ...

  6. Glide 4.9源码解析-图片加载流程

    本文Glide源码基于4.9,版本下载地址如下:Glide 4.9 前言 由于Glide源码真的很复杂,因此本文只分析和贴出与图片加载流程相关的功能以及代码.另外本文Glide源码基于4.9,与3.x ...

  7. java lock可重入_Java源码解析之可重入锁ReentrantLock

    本文基于jdk1.8进行分析. ReentrantLock是一个可重入锁,在ConcurrentHashMap中使用了ReentrantLock. 首先看一下源码中对ReentrantLock的介绍. ...

  8. java商品信息管理系统代码_[源码分享]学生信息管理系统(管理员)

    我一直以为学生信息管理系统是烂大街的,网上一搜一大把的那种 毕竟这种项目是学完C语言之后都可以独立完成的项目,只有界面好看与否的问题 最近好多学生问学生信息管理系统的代码,估计是C语言大作业什么的.然 ...

  9. Java并发编程之AQS以及源码解析

    文章目录 概览 实现思路 实现原理 源自CLH锁 AQS数据模型 CAS操作 主要方法 自定义同步器的实现方法 AQS定义的模板方法 源码解读 等待状态释义 AQS获取锁的流程图 获取独占锁的实现 总 ...

  10. faster rcnn接口_源码解析faster rcnn (mask rcnn)全过程

    1. 总领过程--官方faster cnnn 调用过程 import torchvision, torch # 导入官方faster rcnn 模型 model = torchvision.model ...

最新文章

  1. iOS10.3 的评论系统
  2. css菜单缓慢滑动_如何使用HTML,CSS和JavaScript构建滑动菜单栏
  3. Android Paint应用之自定义View实现进度条控件
  4. LeetCode 5178. 四因数
  5. c 普通的文本变成注释文本的快捷键_phpstrom 快捷键,记一下记一下!(life)
  6. DotNetNuke(DNN)皮肤制作--如何修改DNN中众多的CSS文件
  7. 《防患未然:实施情报先导的信息安全方法与实践》——2.3 情报循环
  8. lzg_ad:使用EWF API开发常见问题
  9. 平面三角形外接圆圆心与半径求解算法
  10. 黑马java学习笔记4 强化部分 常见API
  11. o2o模式都有哪些应用场景?
  12. xmapp启动数据库问题记录 Access denied for user ‘me‘@‘localhost‘ (using password: NO)
  13. 2017初二上期中考试总结
  14. edup无线网卡驱动安装linux,EDUP EP-N8513 (RTL8188CUS芯片)在Ubuntu 12.10下的wifi不能连接问题解决方法...
  15. 【多数据中心】分布式数据同步设计方案
  16. Java中如何实现数组输入和输出
  17. NLP自然语言处理-机器学习和自然语言处理介绍(四)
  18. 爱奇艺《大魔术师》海内外备受关注
  19. 技术前沿与经典文章8:智能家居发展白皮书——全屋智能解决方案
  20. 微信小程序中使用slot插槽

热门文章

  1. 图书销售管理系统数据库SQL应用编程 和 安全管理
  2. KETTLE教程:转换
  3. A very hard mathematic problem(二分)
  4. linux批量修改文件后缀
  5. 高仿iOS微信客户端
  6. 数学建模论文排版大总结
  7. 一款自动生成唯一头像的开源代码库
  8. kafka详细安装运行
  9. #include“stdafx.h”详解
  10. javaweb入门教程