synchronized锁的升级

news2024/9/21 20:23:19

 synchronized锁优化的背景

用锁能够实现数据的安全性,但是会带来性能的下降

无锁能够基于线程并行提升程序性能,带来安全性的下降

java5   synchronized默认是重量级锁,java6以后引入偏向锁和轻量锁,java15 逐步废弃了偏向锁 

锁升级的过程

无锁---》偏向锁(一个线程重复请求)---》轻量锁(cas)---》重量锁

为什么每个对象都可以成为一个锁?

        Monitor 可以理解为一种同步工具,也可以理解为一种同步工具,常常被描述为一个java对象。java对象是天生的Monitor,每个对象都有成为Monitor的潜质,因为在java设计中,每个对象自打娘胎里出来就带着一把看不见的锁,它叫做内部锁或者Monitor锁(管程)。

一、无锁:

初始状态,一个对象被实例化后,如果还没有任何线程竞争锁,那么它就是无锁状态(标志位是001)

    public static void main(String[] args) {
        Object o = new Object();
        System.out.println("10进制:" + o.hashCode());
        System.out.println("16进制:" + Integer.toHexString(o.hashCode()));
        System.out.println("2进制:" + Integer.toBinaryString(o.hashCode()));

        //2进制:1100111011100110010011110110110
        //      1100111011100110010011110110110
        //如果没有调用 hashcode 方法 则Mark Word 的打印结果不会体现出来2进制的hash值
        System.out.println(ClassLayout.parseInstance(o).toPrintable());
    }

 二、偏向锁:

  当一个同步代码块,被同一个线程多次访问,由于只有一个线程,那么该线程在后续访问时便会自动获得锁(标志位101)。偏向锁不涉及用户态(自己写的代码)到内核态(jvm调用的底层代码)的转化,不必要直接升级为最高级(重量锁)。JVM不需要与操作系统协商设置Mutex(争取内核),它只需要记录下线程ID,就标志自己获取了当前锁,不用操作系统接入。在没有其他线程竞争的时候,一直偏向(偏心)当前线程,当前线程可以一直执行。

        结论:当一个线程访问synchronized代码块的时候,JVM使用CAS操作把线程指针ID记录到MarkWord当中,并修改偏向标志,标示当前线程获得该锁。锁对象变成偏向锁(通过CAS修改对象头里锁的标志位),字面意思是“偏向于第一个获得它的线程”的锁,执行完同步代码块后,线程并不会主动释放偏向锁。当该线程第二次到达同步代码块时会判断此时持有的线程是否还是自己(持有锁的线程ID也在对象头里),JVM通过对象的Mark Word 判断:当前线程ID还在,说明还持有着这个对象的锁,就可以继续进入临界区工作。由于之前没有释放锁,这也就不需要重新加锁。如果自始至终使用锁的线程只有一个,很明显偏向锁没有额外开销,性能极高。

         JVM 查看偏向锁基本配置信息命令:java -XX:+PrintFlagsInitial |grep BiasedLock*

        如图:jdk1.8

        

偏向锁在jdk1.6之后是默认启动的,但是启动时间 延时4秒钟

1.所以需要添加jvm参数 -XX: BiasedLockingStartupDelay=0  让其程序启动时立刻启动,不延时。

        开启偏向锁参数:

                -XX:+UseBiasedLocking -XX: BiasedLockingStartupDelay=0

        关闭偏向锁:关闭之后程序默认直接进入 轻量级锁状态

                -XX:-UseBiasedLocking

2.也可以睡眠4秒以上等待偏向锁启动,再观察对象头中的锁标志位

    public static void main(String[] args){
        try {TimeUnit.MILLISECONDS.sleep(5000);} catch (InterruptedException e)     
          {e.printStackTrace();}
        Object o = new Object();
        synchronized (o) {
            System.out.println(ClassLayout.parseInstance(o).toPrintable());
        }
    }

 3.偏向锁的撤销

        当有其他线程逐步来竞争锁时,就不能再使用偏向锁了,而是要升级为轻量锁。偏向锁使用一种等到竞争出现才释放锁的机制,只有当其他线程竞争锁时,持有偏向锁的原来线程才会被撤销。撤销需要等待全局安全点(该时间点没有任何字节码执行),同时检查持有偏向锁的线程是否还在执行。

        ①第一个线程正在执行synchronized代码块,它还没有执行完,其他线程来抢夺,这是偏向锁会被取消掉,升级为轻量级锁,此时轻量级锁由原持有偏向锁的线程持有,继续执行其同步代码块,而正在竞争的线程会进入自旋等待获取该轻量锁。

        ②第一个线程执行完成synchronized代码块,则将对 象头设置为无锁状态并且撤销偏向锁,重新偏向。

三、轻量锁

        有线程参与锁的竞争,但是获取锁的冲突时间极短,本质就是CAS自旋锁(标志位 00),是为了在线程近乎交替执行同步块时提高性能。

        多线程竞争,但是任意时刻最多只有一个线程竞争,既不存在锁竞争太过激烈的情况,也没有现场阻塞。

1.轻量锁的获取:

        假如线程A已经拿到锁,这时线程B又来抢该对象锁,由于该对象锁已经被A拿到,当前该锁已经是偏向锁。而线程B在争抢锁时发现对象头Mark Word中的线程ID 不是线程B自己的线程ID,而是A线程的ID,那么线程B就会进行CAS操作希望能获取到锁。

情况1:如果锁获取成功,直接替换Mark Word中的线程ID 为B自己的ID(A变成B),重新偏向于其他线程(即将偏向锁交给了其他线程,当前线程被释放了锁),该锁会保持偏向状态,A线程OVER,B线程成功上位。

情况2:如果锁获取失败,则偏向锁升级为轻量级锁(设置偏向锁标志位为0,设置锁标志位为00),此时轻量锁是由原来持有偏向锁的线程持有,继续执行同步代码块 ,而正在竞争的线程B会进入自旋等待获取该轻量锁。

        //偏向锁默认延迟4秒启动,所以没有设置偏向锁立即启动,会直接使用轻量锁
        Object o = new Object();
        new Thread(()->{
            synchronized (o){
                System.out.println(ClassLayout.parseInstance(o).toPrintable());
            }
        }).start();

 2.轻量锁与偏向锁的区别和不同

       ①偏向锁只是一个线程,没有其他线程竞争。轻量级锁是两个及以上线程进行争抢。轻量级锁争抢失败时,会自旋尝试获得锁。但是偏向锁只有一个线程,不会自旋。

        ②轻量级锁每次退出同步块时都会释放锁,而偏向锁是在竞争发生时才释放锁。

四、重量级锁

        java中的synchronized重量级锁(标志位10)是基于进入和退出Monitor对象实现的。在编译时会将同步块的开始位置插入monitor enter 指令,在结束位置插入 monitor exit 指令。当线程执行monitor enter指令时,会尝试获取对象所对应的Monitor所有权,如果获取到了,即获取到了锁。会在Monitor 中的ower中记录当前线程的ID,这样它将处于锁定状态,除非退出同步块,否则其他线程无法获取这个Monitor.

        Object o = new Object();
        new Thread(() -> {
            synchronized (o){
                System.out.println(ClassLayout.parseInstance(o).toPrintable());
            }
        }, "t1").start();
        new Thread(() -> {
            synchronized (o){
                System.out.println(ClassLayout.parseInstance(o).toPrintable());
            }
        }, "t2").start();

 五、锁的指向

偏向锁:Mark Word 存储的是偏向的线程ID。

轻量级锁:Mark Word 存储的是指向线程栈帧中Lock Record的指针。

重量级锁:Mark Word 存储的是指向堆中的monitor对象的指针。

六、hashcode问题

        hashcode与偏向锁不共存,如果在偏向锁执行同步代码块时调用获取hashcode的方法,则该偏向锁会立即撤销,偏向锁会膨胀为重量级锁。

七、锁的优缺点

 

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

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

相关文章

机器学习实战(第二版)读书笔记(4)——seq2seq模型注意力机制(BahdanauAttention,LuongAttention)详解

一、Seq2seq模型 机器学习实战(第二版)读书笔记(1)——循环神经网络(RNN) 中详细介绍了RNN如下图1所示,可以发现RNN结构大多数对序列长度比较局限,对于机器翻译等任务(输入输出长度不想等N to M),RNN没办法处理&…

SVN使用:Mac电脑中修改SVN输出信息为英文的方法

前言 作为软件开发人员,关于项目代码管理以及维护想必都不陌生,尤其是在团队协作的时候,多人开发维护同一个项目更是需要代码管理。关于项目代码管理维护工具,常用的就是Git、SVN等管理工具。本篇文章只来分享一下关于SVN的配置设…

C语言学习笔记-常量

“常量”的广义概念是:‘不变化的量’。例如:在计算机程序运行时,不会被程序修改的量。 以上是百度百科上对常量的部分定义。C语言的学习过程中将会接触很多的常量,不同类型的常量其定义、用法等会有所差异。要搞清楚他们的相似与…

如何恢复已删除的文件?5分钟搞定的简单方法。

本文介绍如何使用文件恢复程序恢复已删除的文件。它包括与恢复已删除文件相关的提示。 如何恢复已删除的文件 从硬盘驱动器恢复已删除的文件并不是一件疯狂的事情,但一旦您意识到文件已被删除,就尝试恢复会有所帮助。被删除的文件通常不会被真正删除&am…

终于有人把数据仓库讲明白了

数仓概念 ⚫ 数据仓库(英语:Data Warehouse,简称数仓、DW),是一个用于存储、分析、报告的数据系统。 ⚫ 数据仓库的目的是构建面向分析的集成化数据环境,分析结果为企业提供决策支持(Decision Support&am…

Linux入门教程|| Linux 忘记密码解决方法|| Linux 远程登录

很多朋友经常会忘记Linux系统的root密码,linux系统忘记root密码的情况该怎么办呢?重新安装系统吗?当然不用!进入单用户模式更改一下root密码即可。 步骤如下: 重启linux系统 3 秒之内要按一下回车,出现如…

解决Error: Electron failed to install correctly, please delete......报错的问题

问题 在启动electron项目的时候,报mlgb错 Error: Electron failed to install correctly, please delete node_modules/electron and try installing again 搞了 好久 才解决 原因 升级Electron到7.0.0,提示Electron failed to install correctly, p…

python数据可视化开发(3):使用psutil和socket模块获取电脑系统信息(Mac地址、IP地址、主机名、系统用户、硬盘、CPU、内存、网络)

系列文章目录 python开发低代码数据可视化大屏:pandas.read_excel读取表格python实现直接读取excle数据实现的百度地图标注python数据可视化开发(1):Matplotlib库基础知识python数据可视化开发(2):pandas读取Excel的数据格式处理 文章目录系…

Linux下监控类命令:ps,du,top,df,free详解

Linux下监控类命令top命令top信息解释top参数使用ps命令ps信息解释ps参数使用du和dffree命令top命令 top命令,是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,一般系统资源导致的崩溃问题可以使用top实时监控各进程…

魔兽世界服务器架设开服教程(巫妖王之怒外网详细教程)

魔兽世界服务器架设开服教程(巫妖王之怒外网详细教程)首先需要了解魔兽各个重要文件详细情况说明不管是任何一个魔兽世界GM想要将服务器修改成为自己想要的样子,首先要做的一件事情就是了解自己的服务器文件是说明意思,对于大多数…

2023年java面试之设计模式

1.什么是设计模式设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。2.为什么要学习设计模式看懂源代码:如果你不懂设计模式去看Jd…

云原生周刊 | 使用 K8s 可视化工具集来调试业务 | 2023-1-30

开源项目推荐 k8z k8z 意在 K8s 业务层面,提供一个方便好用的 K8s 集群可视化工具集。目前包含以下功能: 终端:连接到集群任意 Pod 容器上,方便调试Tcpdump:对集群内容器进行 tcpdump 抓包,可直接展示抓…

七天实现一个go web框架

目录引流为什么要用web框架复习下net/http库以及http.Handler接口代码结构General.go启动!上下文必要性封装前context.go拆分router封装后启动!前缀树路由Trie 树目标实现前缀树修改router改变ServeHTTP实现分组控制Group对象的属性其余实现中间件实现其…

云计算|OpenStack|社区版OpenStack安装部署文档(三 --- 身份认证服务keystone安装部署---Rocky版)

一, 什么是keystone keystone是openstack的关键必选组件之一,其作用是提供身份认证服务,所有的身份认证信息都是存储在controller节点的数据库内。 具体的关于keystone的介绍可以参见官方文档:OpenStack Docs: Identity servic…

设备树中的pin 信息,是在什么时候被初始化的?

一、开发环境 SOC : IMX6ULL系统内核:4.1.15 二、问题描述 Linux 内核提供了pinctrl 和gpio 子系统用于GPIO 驱动。pinctrl_ctrl 子系统从设备树中获取pin 信息,然后配置pin复用 和pin电气特征(上/下拉,速度,驱动能…

python小游戏——像素鸟代码开源

♥️作者:小刘在这里 ♥️每天分享云计算网络运维课堂笔记,努力不一定有收获,但一定会有收获加油!一起努力,共赴美好人生! ♥️夕阳下,是最美的,绽放,愿所有的美好&#…

特斯拉Q4财报:底部反弹70%,为信仰打call

北京时间2023年1月26日美股盘后,探案君的信仰之股——特斯拉公布了2022年第四季度财报,无论营收还是利润依然吊打华尔街预期,这就是特斯拉,当然这也很特斯拉! 一、整体业绩,很特斯拉 营收方面&#xff1a…

JavaScript基础复盘4

JavaScript作用域 JavaScript作用域就是代码名字(变量)在某个范围内起作用和效果 目的是为了提高程序的可靠性,减少命名冲突。 JS没有块级作用域,{}内写的变量外部也可以使用。 作用域链 作用域链:内部函数访问外部函数…

拯救OOM~ 字节自研 Android 虚拟机内存管理优化黑科技 mSponge

本文描述的虚拟机内存管理优化方案,是从应用侧视角对 Android 虚拟机内存管理进行改造,优化了虚拟机对 LargeObjectSpace 的内存管理策略,间接增加其它内存空间使用上限。改造后的方案,32 位运行环境 LargeObjectSpace 的内存使用…

Aspose.CAD for .NET 23.1.0 Crack

Aspose.CAD for .NET 是一个独立的类库,它增强了 Windows 和 ASP.NET 的应用程序,以便在不需要 AutoCAD 甚至任何其他工作流渲染过程的情况下处理和渲染 CAD 绘图。CAD 类库允许将 DWG、DWT、DWXF、IFC、PLT、DGN、OBJ、STL、IGES、CFF2 文件以及图层和布…