一文打通锁升级(偏向锁,轻量级锁,重量级锁)

简介: 一文打通锁升级(偏向锁,轻量级锁,重量级锁)

前置知识:synchronized

在JavaSE1.6以前,synchronized都被称为重量级锁。但是在JavaSE1.6的时候,对synchronized进行了优化,引入了偏向锁和轻量级锁,以及锁的存储结构和升级过程,减少了获取锁和释放锁的性能消耗,有些情况下它也就不那么重了。

在同步方法中,使用了flag标记ACC_SYNCHRONIZED,当调用方法时,调用指令会检查方法的ACC_SYNCHRONIZED访问标志是否设置。如果设置了,执行线程先持有同步锁,然后执行方法,最后在方法执行完毕释放锁。

同步代码块是使用monitorenter和monitorexit指令进行同步的。monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处,JVM要保证每个monitorenter必须有对应的monitorexit与之配对。任何对象都有一个monitor与之关联,并且一个monitor被持有后,它将处于锁定状态。线程执行到monitorenter指令时,将会尝试获取对象的monitor所有权,即尝试获取对象的锁。

synchronized锁升级

synchronized锁优化的背景:
用锁能实现数据的安全性,但是会带来性能下降。
无锁能够基于线程并行提升程序性能,但是会带来线程安全性下降。synchronized锁:根据对象头的mark word 锁标志位来确定当前属于哪一种锁。

为什么会存在锁升级现象?

在java5及其以前,只有synchronized,这个是重量级锁,是操作系统级别的重量级操作。假如锁的竞争比较激烈,性能下降。因为存在用户态和内核态之间的转换。

java的线程是映射到操作系统原生线程之上的,如果要阻塞或唤醒一个线程就需要操作系统介入,需要在户态与核心态之间切换,这种切换会消耗大量的系统资源,因为用户态与内核态都有各自专用的内存空间,专用的寄存器等,用户态切换至内核态需要传递给许多变量、参数给内核,内核也需要保护好用户态在切换时的一些寄存器值、变量等,以便内核态调用结束后切换回用户态继续工作。

发布Java早期版本中,synchronized属于重量级锁,效率低下,因为监视器锁(montor)是依赖于底层的操作系统的MutexLoCK(系统工斥量)来实现的,挂起线程和恢复线程都需要转入内核态去完成,阻塞或唤醒一个Java线程需要换作系统切换CPU状态来完成,这种状态切换需要耗费处理器时间,如果同步代码块中内容过于简单,这种切换的时间可能比用户代码执行的时间还长”,时间成本相对较高。这也是为什么早期的synchronized效率低的原因Java 6之后,为了减少获得锁和释放锁所带来的性能消耗,引入了轻量级锁和偏向锁

偏向锁

顾名思义,它会偏向于第一个访问锁的线程

当一段同步代码一直被同一个线程多次访问,由于只有一个线程那么该线程在后续访问时便会自动获得锁,在多线程的情况下,锁不仅不存在多线程竞争,还存在锁由同一个线程多次获取,偏向锁解释在这种情况下出现的,她的出现是未来解决只有在一个线程执行同步时提高性能

  • 如果在运行过程中,同步锁只有一个线程访问,不存在多线程争用的情况,则线程是不需要触发同步的,这种情况下,就会给线程加一个偏向锁。线程第二次到达同步代码块时,会判断此时持有锁的线程是否就是自己,如果是则正常往下执行。由于之前没有释放锁,这里也就不需要重新加锁。如果自始至终使用锁的线程只有一个,很明显偏向锁几乎没有额外开销,性能极高。(避免了线程的上下切换),也即偏向锁在资源没有竞争情况下消除了同步语句,懒的连CAS操作都不做了,直接提高程序性能。
  • 如果在运行过程中,遇到了其他线程抢占锁,则持有偏向锁的线程会被挂起,JVM会消除它身上的偏向锁,将锁恢复到标准的轻量级锁。偏向锁通过消除资源无竞争情况下的同步原语,进一步提高了程序的运行性能。一旦有第二个线程加入锁竞争,偏向锁就升级为轻量级锁(自旋锁)。升级为轻量级锁的时候需要撤销偏向锁,撤销偏向锁的时候会导致STW(stop the word)操作;

实际上偏向锁在JDK1.6之后是默认开启的,但是启动时间有延迟(4秒),
所以需要添加参数-XX:BiasedLockingStartupDelay=0,让其在程序启动时立刻启动

开启偏向锁:

-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0

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

-XX:-UseBiasedLocking

轻量级锁

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

有线程来参与锁的竞争,但是获取锁的冲突时间极短。本质就是自旋锁CAS

主要目的: 在没有多线程竞争的前提下,通过CAS减少重量级锁使用操作系统互斥量产生的性能消耗,说白了先自旋,不行才升级阻塞。

升级时机:当关闭偏向锁功能或多线程竞争偏向锁会导致偏向锁升级为轻量级锁

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

此时线程B操作中有两种情况:

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

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

Java6之前

默认启用,默认情况下自旋的次数是10次,或者自旋线程数超过cpu核数一半。

Java6之后

变为自适应自旋锁。意味着自旋的次数不是固定不变的,而是根据:拥有锁线程的状态来决定,或者同一个锁上一次自旋的时间。

线程如果自旋成功了,那下次自旋的最大次数会增加,因为JVM认为既然上次成功了,那么这一次也很大概率会成功。反之,如果很少会自旋成功,那么下次会减少自旋的次数甚至不自旋,避免CPU空转。

重量级锁

适用于:有大量的线程参与锁的竞争,冲突性很高。

重量级锁原理

Java中synchronized的重量级锁,是基于进入和退出Monitor对象实现的。在编译时会将同步块的开始位置插入monitor enter指令,在结束位置插入monitor exit指令。

当线程执行到monitor enter指令时,会尝试获取对象所对应的Monitor所有权,如果获取到了,即获取到了锁,会在Monitor的owner中存放当前线程的id,这样它将处于锁定状态,除非退出同步块,否则其他线程无法获取到这个Monitor。


相关文章
IntelliJ IDEA - 在选中的范围内搜索关键字
IntelliJ IDEA - 在选中的范围内搜索关键字
1188 0
IntelliJ IDEA - 在选中的范围内搜索关键字
|
Ubuntu
LLVM编译源码
LLVM编译源码
337 0
|
缓存 Java Maven
Maven配置多仓库无效?来看看这篇文章
Maven配置多仓库无效?来看看这篇文章
1714 0
Maven配置多仓库无效?来看看这篇文章
|
虚拟化 Docker Windows
VMware Workstation Pro 启动虚拟机报错,您的主机不满足在启用 Hyper-V 或 Device/Credential Guard 的情况下运行 VMware Workstatio
原因 出现此问题可能是您安装的docker或其他软件启用了Hyper-V,导致虚拟机无法启动 解决方法 右键开始,选择【应用和功能】
2445 0
VMware Workstation Pro 启动虚拟机报错,您的主机不满足在启用 Hyper-V 或 Device/Credential Guard 的情况下运行 VMware Workstatio
|
7月前
|
Java 应用服务中间件 Python
OS Copilot全面测评报告 与 利用OS Copilot进行在线WebAPI接口开发
OS Copilot 是一款智能操作系统助手,具备智能、专业、高效和协作四大优势。本文通过全面测评其功能,验证了其在系统诊断、调优及编程等场景中的表现,并通过在线WebAPI接口开发测试进一步评估其快速接口开发上线的能力。具体步骤包括ECS环境配置、OS Copilot安装与配置、-t/-f参数功能测试、管道测试以及利用OS Copilot进行Python WebAPI开发。最终结果显示,OS Copilot能够显著提升开发效率,解决了便捷开发中的临时接口开发问题,整体体验非常满意。建议增加-s参数以保存上下文,进一步增强交互性。
223 12
OS Copilot全面测评报告 与 利用OS Copilot进行在线WebAPI接口开发
|
JavaScript Java 测试技术
基于小程序的家政服务管理系统+springboot+vue.js附带文章和源代码设计说明文档ppt
基于小程序的家政服务管理系统+springboot+vue.js附带文章和源代码设计说明文档ppt
326 59
|
缓存 监控 网络协议
在Linux中,有哪些常用的网络管理工具?
在Linux中,有哪些常用的网络管理工具?
|
Sentinel
Sentinel实现黑白名单控制详细教程来了
Sentinel实现黑白名单控制详细教程来了
339 0
|
敏捷开发 测试技术 持续交付
深入理解与应用软件测试中的持续集成策略
【5月更文挑战第28天】本文旨在探讨持续集成(CI)在现代软件开发周期中的重要性及其对软件测试实践的影响。通过分析持续集成的基本原则、流程和工具,我们揭示了其如何提高软件质量、加速反馈循环以及促进团队协作。文章还将讨论实施持续集成时面临的挑战及应对策略,为读者提供一套全面的指导框架,以优化他们的测试流程并提升软件交付效率。
|
缓存 负载均衡 数据库
优化后端性能:提升Web应用响应速度的关键策略
在当今数字化时代,Web应用的性能对于用户体验至关重要。本文探讨了如何通过优化后端架构和技术手段,提升Web应用的响应速度。从数据库优化、缓存机制到异步处理等多个方面进行了深入分析,并提出了一系列实用的优化策略,以帮助开发者更好地应对日益增长的用户访问量和复杂的业务需求。
745 28