重回synchronized(源码解读与实战解析篇)

news2024/11/25 4:34:57

读前必知

文中锁,也称为对象锁,而锁对象就是指的承载这个锁的对象,如下面,用法中所指的Object o,在print3中就是锁对象。
以下源码分析仅适用于jdk8,版本未知,因为源码提供者的源码版本访问地址出错了
https://github.com/farmerjohngit/myblog/issues/12 源码提供者地址,配合源码服用更不容易懵

用法

我们都知道synchronized,可以加在对象或者方法头上
1 加在对象头上,那么这个对象就无法被再次上锁。
2 加在非静态方法头上,等同于加在当前对象头上。
3 加在静态方法头上,等同于加在当前对象的类对象头上。
**注意:**加在类对象头上并不会导致,这个类的任何实例对象无法上锁。即Class对象上锁与对象上锁无关。

public class Test {

    public Object o = new Object();

    public synchronized void print1(){
        System.out.println("进入");
    }

    public synchronized static void print2(){
            System.out.println("进入");
    }

    public void print3(){
        synchronized (o){
            System.out.println();
        }
    }

    public static void main(String[] args) {
        
    }
}

字节码

在这里插入图片描述
这是三个print的字节码结构,我们从中可以得出两点
1 如果方法使用了synchronized,那么它的flag会增加ACC_SYNCHRONIZED标识
这是因为ACC_SYNCHRONIZED可以提醒方法在进入之前获取相应的对象锁,在返回前释放对象锁。
2 如果使用了synchronized代码块,那么会出现一次monitorenter和两次monitorexit,这是为了防止异常返回而造成锁未释放。如图,如果正常执行,那么17行将会跳转到25行直接完成return,而如果不正常执行,那么7-17行如果出错,就会前往20行,将栈顶元素清空,然后放入锁对象,再执行monitorexit,再完成return。

获取锁流程

初始化

如果这个对象锁被获取过,并且没有释放,那么会从线程栈底到正在获取锁的lock record间寻找一个空闲的的lock record,找不到,会扩建栈,然后循环,直到上面那个获取结束。
如果这个对象锁没有被获取过,那么会从线程栈底到栈顶寻找一个空闲的lock record,用于后续操作。

这里有三点需要注意,一,获取的空闲lock record会是最接近顶的那个,这样可以避免第一种空耗情况的发生。二,无论是哪种情况,如果找不到空闲的lock record,都会给栈分配一个新的lock record空间。三,这个栈不是虚拟机栈也不会是操作数栈,虚拟机栈放的是方法,操作数栈放的是正在执行的操作数,都是一直需要在使用的结构,和这个栈无法共,但这个栈一定是线程私有的。
逐一查询为这次锁获取一个没有使用的lock record,如果没有找到,那么会生成一个全新的lock record存放于栈中,然后将这个lock record指向锁对象,正式进入锁的判断流程。

重入锁这样做的好处有两个,一,保证了最后一个被释放的就是第一次进入的锁,二,避免了过多向上遍历导致性能过低。

偏向锁

进入判断,如果自己是锁拥有者,则直接进入锁内代码块。如果不是自己,会尝试一下将threadId替换进去对象头的markword,如果尝试失败,就会进入锁升级流程,会在线程安全点尝试撤销偏向锁,并将锁对象改成轻量级锁,如果尝试成功,说明是一个匿名偏向锁,直接进入锁内代码块。

轻量级锁

会将锁对象的无锁状态mark word记录在自己displaced markword,如果发现锁对象恰好就是无锁状态,那么让锁对象markword前部分改为指向自己的指针(后面的留作标识为轻量级锁),如果不是,则证明着存在竞争,那么会进入重量级锁。

重量级锁

首先会将markword改为inflating模式,这是一条cmpxchg_ptr指令,保证了只有一个线程可以完成这个步骤。在这个模式下,所有后入线程都会进入循环等待,直到修改inflating模式的线程完成了monitor的初始化工作,然后取得monitor返回。完成初始化的线程也是返回monitor。

于是所有线程都会使用monitor.enter()来获取锁。

重量级锁时会维护一个monitor容器,里面主要存放着cxq,waitset和entrylist三种数据结构,其中head属性存放着自己的displaced markword,没错就是无锁那个,owner存放着定位到拥有者的句柄,可以是thread指针,markword中存放的lock record地址指针。然后将锁对象的mark word改为指向monitor。cxq是一个单向队列,entrylist是一个双向队列。
如果获取失败,那么会将自己放入cxq队列头部,如果获取成功,则会将自己从cxq或者entrylist队列中删除。
如果在同步块中调用了wait(),那么会被放入waitset,如果被notify,那么会放入entrylist。

释放锁流程

最初的开始

遍历整个虚拟机栈,找到所有指向该锁对象的lock record,将每个符合的lock record逐个指向设置为null,进行锁释放,释放流程如下

偏向锁释放

检查一下mark word最后是否是偏向位,如果是,直接返回,只需要lock record设置指向null过就行了。

轻量级锁释放

如果Displaced Mark Word为null,说明是一个重入锁,直接返回,如果不是null,那么就比较mark word是否指向自己。
如果指向自己的话,将Displaced Mark Word替换过去,然后返回,不是的话,那就只剩下一种情况,线程持有锁的过程中,其它线程将锁膨胀成了重量级锁。

重量级锁の释放,公平与非公平?

进入重量级锁,如果自己不是锁的持有线程,那么会判断锁的owner是否指向了自己的lock record,如果是,那么会将自己也改为重量级锁,然后进入唤醒。需要注意的是,这时候并不需要考虑重入,因为走到这一步Displaced Mark Word一定不是null,能走到这一步的一定是第一个获取到锁的那个lock record。
重量级锁的唤醒策略非常多,由一个Knob_QMode的参数决定,参数为2,那么会优先唤醒cxq里的元素,成功直接返回。
参数为3,会将cxq的元素放入entrylist队尾,如果参数为4,那么会将cxq元素放入entrylist队头。
在此之后,唤醒entrylist队头元素。

看上去选择了参数3就可以做到了公平锁真是这样吗?不然。
首先,锁释放流程中,第一步就是释放锁,然后再完成后续唤醒流程,这给了后面获取锁的线程直接占有的机会。
其次,参数3的放入非常粗暴,把这个cxq单链表直接增加了prev指针,变成了双向链表,然后把cxq头节点(也就是最后进入的那个)的prev指针指向了entrylist尾节点,这样做的好处是,直接使用了现有结构,不需要额外空间。
从中我们也可以发现开发者的一个恶趣味,明明可以让cxq最先插入的节点和entrylist尾节点进行连接,但是却要让cxq最后插入的节点与其连接,相当于把整个cxq的顺序都在entrylist中反过来了,把不公平性发挥到了极致。
以下也提供了尾插代码供观赏。
在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/28004.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

金仓数据库KingbaseES GIN 索引

目录 一、索引的逻辑结构 二、索引的物理结构 三、GIN索引使用例子 1、前后模糊查询 2、全文检索 四、gin 索引可用于超长的字段 GIN(Generalized Inverted Index, 通用倒排索引) 是一个存储对(key, posting list)集合的索引结构,其中key是一个键值&#xff0c…

【uniapp】利用Vuex实现购物车功能

实战项目名称:实现购物车功能 文章目录一、实战步骤1. 先编辑store.js文件2. 定义方法和基本的结构3. 编写SETSHPPING二、在项目中调用1. 触发相应的mutations2. 利用computed计算数量和总价的方法提示:本实战内容大部分为具体实现的思路,界面…

C语言 2 —— 常量

常量是什么? 常量就是在程序运行过程中,值不会发生改变,而且一眼可以辨识出值的量。 如: 20,‘a’ , 3.1415926 , "helloworld" 常量的分类: 整形,浮点型,字符型&#…

成电860考研专业课考前划重点-学长课程音频转文字-用科大讯飞花钱买的-三万五千字

成电860考研专业课考前划重点-学长课程音频转文字。 这个是我2021年把视频的音频扒下来后用科大讯飞音频转文字网站上花了几十块钱买的。 说话人1 03:04 对有回放,我这边开回放了,大家可以让大家下载,然后我怕这边回放有问题,大家…

Airtest poco 入门小结

目录 一、poco介绍 1、poco能做什么 2、三种定位方式 1)基本选择器 2)相对选择器 3)空间顺序选择器 4)正则表达式方式 3、poco支持平台 二、Airtest介绍 1、Airtest能做什么 2、Airtest不能做什么 3、Airtest的图像识别…

发挥数字化平台优势,电子元器件采购商城系统助力企业改变固有低效流程

我国是全球最大的电子元器件生产国,电子元器件在国民经济发展中占据着重要地位。近年来,随着数字经济的快速发展,电子元器件的需求量也在不断升高,但疫情与国际环境对电子元器件产业要素流通仍然造成了一定困扰。在此背景下&#…

win10系统下使用onnxruntime部署yolov5模型

文章目录前言一、环境1、硬件2、软件二、YOLO模型三、新建Qt项目1、pro文件2、mainwindow.h3、mainwindow.cpp四、YOLO 类封装1、yolov5.h2、yolov5.cpp3、class.names五、效果前言 上一篇介绍过使用opencv-dnn模块实现模型推理部署,但视频效果较差,本篇…

ps2023最新版免费滤镜插件Exposure安装下载教程

滤镜插件是ps的重要功能之一,它主要是用来制作不同的图片特效。那么,ps滤镜插件哪些好用,ps滤镜插件如何获取,下面我们一起来学习这些内容。 ps滤镜插件是比较多的,下面对几款常见的ps滤镜插件进行讲解,看…

TIA博途中的TRACE功能具体使用方法示例

TIA博途中的TRACE功能具体使用方法示例 我们可以利用TRACE曲线来监控程序、排查故障,那么具体怎样使用呢,可以参考以下内容。 如下图所示,打开TIA博途,新建项目后,在左侧项目树中可以看到TRACES, 如下图所示,双击添加新轨迹,然后在右侧窗口中,添加需要监视的信号,…

TOUGH2系列建模方法及在CO2地质封存、水文地球化学、地热、地下水污染等领域中的技术

TOUGH2系列软件是由美国劳伦斯伯克利实验室开发的,旨在解决非饱和带中地下水、热运移的通用模拟软件。和传统地下水模拟软件Feflow和Modflow不同,TOUGH2系列软件采用模块化设计和有限积分差网格剖分方法,通过配合不同EOS模块,软件…

【矩阵论】3. 矩阵函数——矩阵函数求导

3.6 矩阵函数求导 3.6.1 积分与求导定义 设 mnm\times nmn 阶矩阵 A(x)(aij(x))mnA(x)\left(a_{ij}(x)\right)_{m\times n}A(x)(aij​(x))mn​ 中的元素都是 x 的可导函数,则 A(x)A(x)A(x) 为关于 xxx 的求导为: A′(A)dA(x)dx(daij(x)dx)mnA(A)\frac{…

正点原子stm32F407学习笔记5——串口通信实验

一、串口通信实验1 上位机给开发板发送数据,开发板将收到的数据发回给上位机 串口设置的一般步骤可以总结为如下几个步骤: 串口时钟使能,GPIO 时钟使能。设置引脚复用器映射:调用 GPIO_PinAFConfig 函数。GPIO 初始化设置&#…

数据库性能翻3倍:Redis on Flash分层存储技术是如何做到的?

Redis on flash简介:Redis on Flash 涉及到的是Redis的分层存储技术,即将数据存放在不同地方。Redis自2016年以来支持Redis on Flash。从2019年开始, Redis企业版(Redis Enterprise)宣布支持英特尔Optane DC持久性内存&#xff0c…

基于NB-IoT的智能垃圾桶系统设计与实现

本设计是基于物联网的智能垃圾桶,主要实现以下功能: 1,压力传感器模块采集垃圾重量数据; 2,GPS定位模块采集垃圾桶所在的经纬度数据; 3,人体红外模块检测人体并返回是否有人通过的数据&#xf…

会议管理系统SSM记录(二)

目录: (1)整合Freemarker (2)用户登录 (3)提取头部 (4)提取菜单抽取 (1)整合Freemarker 在pom.xml中加入Freemark依赖: 创建free…

HTML+CSS大作业:基于HMTL校园学校网页设计题材【我的学校网站】

🎉精彩专栏推荐 💭文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业: 【📚毕设项目精品实战案例 (10…

专精特新企业三个层级

专精特新企业也是分层级的。工信部2022年6月印发《优质中小企业梯度培育管理暂行办法》里面明确提出中小企业培育的3个梯度,分别是创新型中小企业、专精特新中小企业和专精特新小巨人企业,刚好构成中小企业发展层级金字塔。这就意味着企业想要发展崛起&a…

19 04-读取DTC快照信息

诊断协议那些事儿 诊断协议那些事儿专栏系列文章,19服务作为UDS中子功能最多的服务,一共有28种子功能,本文将介绍常用的19 04服务:读取快照信息。 关联文章: 19服务List 19 01-通过状态掩码读取DTC数目 $19服务:DTC…

1.2 C++编译器对指针的解释方式(深度理解c++指针)

1.2 指针 1.2.1 指针解释方式 从内存的角度,一个指向类对象的指针与一个指向整数类型的指针或一个指向数组的指针,三者之间是没有任何区别的,它们内部都只存储了一个机器地址值(word)。不同类型指针的区别仅在于其寻址出来的object类型的不…

div+css布局实现个人网页设计(HTML期末作业)

🎉精彩专栏推荐👇🏻👇🏻👇🏻 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业…