偏向锁 10 连问,被问懵圈了。。

简介: 偏向锁 10 连问,被问懵圈了。。

前言

对于Hotpot JVM中的偏向锁,大部分开发者都比较熟悉或者至少听说过。那我们用下面10个关于偏向锁的进阶问题,检验一下自己离精通还有多远。


  • 如何判断当前锁对象为偏向锁
  • 偏向锁如何判断锁重入
  • 当代码运行至synchronized修饰的代码块时,符合什么条件才会尝试获取偏向锁
  • 线程进入偏向锁后,会不会创建lock record
  • 偏向锁膨胀后,lock record有什么变化
  • 如何判断当前持有锁的线程已经因为批量重偏向,而被撤销了偏向锁
  • 批量撤销和批量重偏向的触发条件是什么
  • 批量重偏向后,lock record和锁对象有什么变化
  • 批量撤销后,lock record和锁对象有什么变化
  • 批量撤销/重偏向后,新创建的锁对象,是否支持偏向锁




看了上面的问题,如果是胸有成竹,那就可以跳过这篇文章了。如果一脸问号,这篇文章应该对你有所帮助。


名词解释

首先明确下文章中用到的名词,因为不同人可能叫法不一样。


对象头,Java对象在堆中存储时,会按照对象头加实例数据的结构来存储。这篇文章只讲锁,所以一般是指对象头中的Markword部分。


klass对象,jvm在加载类之后,会在堆内存中生成该类的对象,就是我们代码中this.getClass()获取的对象。


锁对象, synchronized指定的锁对象。对于普通方法,这个对象默认是this指针。对于静态方法,锁对象是堆里的class对象。


Lock record,进入synchronized时在线程栈中生成的锁记录,对这个不熟悉的可以百度一下或看一下《深入java虚拟机》这本书


锁膨胀,hotspot中从轻量级锁升级成重量级锁称之为膨胀,为了便于理解,通常把偏向锁升级成轻量级锁也称为膨胀。


问题解析

问题1:如何判断当前锁对象为偏向锁


这个问题比较简单,一般了解过对象头或者偏向锁的都比较熟悉。当锁对象为偏向锁时,Markword的偏向锁标识位为1,锁标识位为01。即markword的最后3位为101。


image.png


问题2:偏向锁如何判断锁重入


接上面问题的Markword结构,当已经有线程获取到偏向锁,它的id就会填到markword中的线程id中。重入时线程只要检查thread id里存的是否就是自己线程的id就可以了。


问题3:符合什么条件才会尝试获取偏向锁


首先,hotspot中通过参数UseBiasedLocking控制是否启用偏向锁,不设置时默认是启用的。如果想要禁用偏向锁,可以在启动参数中添加-XX:-UseBiasedLocking。


是不是这样回答这个问题就结束了呢?答案是否定的。hotspot还有一个延迟偏向的概念,就是在jvm启动的时候是有一个延迟时间,过了这段时间后偏向锁才开始启用。这个延迟时间通过启动参数BiasedLockingStartupDelay来设置,默认为4秒。那延迟的目的是什么呢?hotspot的解释是在jvm启动过程中,内部有多个逻辑会用到锁,比如类加载。如果一开始就启用偏向锁,就导致频繁的撤销偏向锁,偏向锁的撤销需要在安全点执行,这样有可能影响jvm启动的速度。


满足上面2个条件之后,是不是就愉快的进入偏向锁了呢,其实还要经过2关。


第三个条件就是锁对象没有膨胀,如果锁对象已经膨胀成轻量级锁了,那就不会再走偏向锁了。这就是经常说的锁只支持升级,不支持降级。轻量级锁的markword如下:


image.png


最后,如果锁对象对应的class发生了批量撤销的动作,也不会再进入偏向锁了。比如有10个锁对象lockobj0..lockobj9,他们都是LockObj类的实例,如果发生偏向锁的批量撤销,那在这10个锁对象上的抢锁操作都不会再走偏向锁逻辑。


image.png


问题4:线程进入偏向锁后,会不会创建lock record


了解轻量级锁逻辑的都知道,轻量级锁加锁后,锁对象会保存lock record的引用,关系如下:


image.png


那偏向锁有没有呢?答案是有的。其实轻量级锁的这个lock record在运行至synchronized的时候就创建了,这个时候jvm还不知道具体使用的是偏向锁还是轻量级锁,偏向锁和轻量级锁用的是同一个lock record。偏向锁的时候,对象头里没有lock record的指针。


image.png


但是,我们再深挖一层,是不是每次都会创建?答案是否定的。比如在同一个方法中,对同一个锁对象的重入,就不会再次创建lock record,比如下面的代码(虽然不会有人这么写代码😄):


 

public void testSync() {
        synchronized (this) {
            //first time
            synchronized (this) {
                // second time
            }
        }
    }



问题5:偏向锁膨胀后,lock record有什么变化


首先,来看下膨胀前的lock record和锁对象,它们的关系如下:


image.png


栈中的lock record包含了指向锁对象的指针和markword的副本。 锁膨胀后可能出现两种情况:


1)抢锁线程获得了轻量级锁,则替换lock record中的displace_header的锁状态位为无锁。


2)如果是轻量级锁的锁重入,则会降lock record的displace_header设置为空


3)其它线程持有轻量级锁,则会膨胀成重量级锁,这时候lock record已经没用了,会将将markword锁标记为设置为011,代表已经不使用了


问题6:如何判断持有锁的线程已经因批量重偏向被撤销


当发生批量重偏向时,jvm会将klass对象的markword.epoch+1。并且遍历所有该类型的锁对象,如果加锁的线程仍然存活,则也会将锁对象的epoch设置成跟klass一样。


所以,如果另外一个线程在进入偏向锁逻辑时,发下锁对象的epoch跟klass的epoch不相等,则可以肯定该偏向锁已经被撤销。


问题7:批量撤销和批量重偏向的触发条件是什么


jvm通过两个参数来控制何时触发批量重偏向和批量撤销。


BiasedLockingBulkRebiasThreshold,批量偏向阈值,默认值20。

BiasedLockingBulkRevokeThreshold,批量撤销阈值,默认值40。

当同一类型的锁对象上发生锁争抢累计达到这两个数字时就会触发批量重定向和批量撤销。


划重点,这两个累计值是在klass对象上,不是锁对象上。


问题8:批量重偏向后,lock record和锁对象有什么变化


可以参考问题6,批量重偏向后,klass对象和仍然活着的线程持有的锁对象,epoch会加1。也就是说,当前线程抢的偏向锁的持有线程如果挂了,那epoch不会变,就会被抢锁线程撤销或重偏向到当前线程。


问题9:批量撤销后,lock record和锁对象有什么变化


批量撤销后,klass和所有相同锁对象的偏向锁都会被撤销,markword的锁标识位变成无锁。


问题10:批量撤销/重偏向后,新创建的锁对象,是否支持偏向锁


批量重偏向后,新创建的锁对象,默认仍然是偏向锁。

批量撤销后,新创建的锁对象,默认都会是轻量级锁(无锁)。因为发生批量撤销后,klass对象的markword锁标识位变成无锁,所以在这之后创建的锁对象,默认跟klass对象的markword相同。

总结

jvm因为加入了偏向锁逻辑而大大提高了同步锁的速度。但是偏向锁不是万能的,尤其是现在互联网应用并发越来越高,偏向锁在过多的争抢下反而会影响效率并且很快就会发生膨胀,已经越来越偏离了了它设计时的初衷。当前的Java应用中也基本会使用JUC包来做并发的同步,偏向锁的使用场景越来越少。当然硬件性能的提升也在削弱偏向锁的优势,所以Java15默认关闭了偏向锁。当然,本篇文章对于你参加面试还是能够提供一点点帮助的。


原文:jianshu.com/p/a76a6c6d68e1



相关文章
配置project.config.json文件报错 解析 project.config.json 文件失败,请检查其内容或删除此文件。
配置project.config.json文件报错 解析 project.config.json 文件失败,请检查其内容或删除此文件。
1047 0
配置project.config.json文件报错 解析 project.config.json 文件失败,请检查其内容或删除此文件。
|
12月前
|
机器学习/深度学习 数据采集 数据可视化
基于YOLOv8的粉尘污染检测识别|完整源码数据集+PyQt5界面+完整训练流程+开箱即用!
随着工业化的快速发展,粉尘污染成为了环境监测和工业安全中必须关注的问题。传统的粉尘检测方法往往依赖人工采样和实验室分析,周期长、成本高。基于计算机视觉的自动化粉尘检测系统能够实时、准确地监控环境,从而极大提高安全和管理效率。本项目基于最新的YOLOv8算法,实现了粉尘污染检测与识别,并提供完整源码、数据集和可视化界面,实现开箱即用。
基于YOLOv8的粉尘污染检测识别|完整源码数据集+PyQt5界面+完整训练流程+开箱即用!
|
移动开发 安全 搜索推荐
深度链接(Deep Link)实战指南:何时做?如何做?一篇讲透
本文探讨了 App 是否需要实现深度链接的问题,从产品复杂度、营销需求和用户旅程三个维度分析,帮助判断深度链接的最佳接入时机,并提供实操建议。
778 5
|
存储 安全 数据中心
数据中心概述
数据中心是包含计算机系统、通信设备及配套环境控制、安全装置的复杂设施,用于集中存储、处理和传输数据。根据用途可分为企业、运营商和互联网数据中心。其基础设施涵盖机房、供电、散热、网络设备及管理软件。发展历程从大型机时代逐步演进至云计算时代,现以大规模、资源池化为特征。
1278 0
|
新能源 API 开发者
车辆限行查询API的实战指南:让限行管理从此 “有码可循”
随着全国机动车保有量突破4.53亿辆,交通拥堵与污染问题日益严峻,各城市陆续实施限行政策。探数API推出的车辆限行查询服务覆盖200+城市,提供实时限行数据,包括本地/外地燃油车及新能源车的限行规则、区域和时间等信息。其功能涵盖单个城市限行政策查询与支持城市的全面列表,助力用户精准规划出行。通过HTTP POST请求即可轻松接入,适用于导航平台和个人开发者。在“双碳”目标下,该API推动绿色出行与智能交通发展,为个人、企业和城市治理提供高效解决方案。
982 5
|
算法 搜索推荐 API
京东拍立淘图片搜索 API 接口使用指南:从原理到实践
京东拍立淘图片搜索API,基于先进图像识别技术,支持上传图片、URL或拍摄实物搜索相似商品。其特点包括:搜索便捷高效,用户可快速发起搜索;精准匹配结果,通过算法捕捉商品特征确保准确;数据覆盖广泛,依托京东海量商品资源满足个性化需求;智能推荐拓展,根据用户行为挖掘潜在需求,提升购物体验。
|
人工智能 大数据 开发者
阿里云技术解决方案开放免费试用,热门AI场景免费体验!
阿里云推出免费试用计划,2025年7月起,新老用户均可领取100点试用点,用于部署体验技术解决方案。完成部署还可再获最高100点,相当于一年200元云资源免费用。支持AI、大数据、安全等多个领域,涵盖DeepSeek部署、模型微调等热门场景。点击链接即可领取,快速上手云上方案。
|
数据可视化 Python
以下是一些常用的图表类型及其Python代码示例,使用Matplotlib和Seaborn库。
通过这些思维导图和分析说明表,您可以更直观地理解和选择适合的数据可视化图表类型,帮助更有效地展示和分析数据。
835 8
|
Java 程序员
重磅!阿里发布《Java开发手册(泰山版)》
最近,阿里的《Java开发手册》又更新了,这个版本历经一年的修炼,取名:《Java开发手册(泰山版)》正式出道。 正所谓无规矩不成方圆,在程序员的世界里,也存在很多规范,阿里出版的Java开发手册就是其中之一,从各个方面都约束了程序员该如何有规矩的写代码,以及如何写好代码。
2205 0
重磅!阿里发布《Java开发手册(泰山版)》