当前位置: 首页 > news >正文

synchronized锁升级机制

文章目录

  • 前言
  • 传统重量级锁存在的问题
  • 锁升级的基本思路
  • 偏向锁
  • 轻量级锁
    • 自旋的优化策略
  • 重量级锁
  • 锁升级带来的实际价值

前言

在日常开发中,我们经常使用synchronized来保证线程安全,但很多人可能不知道这个关键字在JVM内部经历了怎样的优化过程。早期版本的synchronized性能确实不太理想,但现在已经通过锁升级机制得到了很大改善。

传统重量级锁存在的问题

最初的synchronized实现相当简单粗暴,不管什么情况都直接使用操作系统提供的互斥量,这就意味着每次加锁解锁都要进行系统调用。系统调用需要从用户态切换到内核态,这个过程开销很大。而且一旦锁被占用,其他线程就会被阻塞,需要操作系统来调度唤醒,又是一笔不小的开销。

锁升级的基本思路

JVM发现了这个问题并想出了解决方案:根据实际的竞争情况来选择不同的锁实现。如果没有竞争或竞争很少,就用轻量级的方式;如果竞争激烈,再升级到重量级锁。
这个升级过程是这样的:无锁状态 → 偏向锁 → 轻量级锁 → 重量级锁
每个Java对象都有一个对象头,里面有个叫Mark Word的区域专门用来存储锁的信息。

偏向锁

偏向锁的设计思路很直接:既然大部分情况下同步块都只有一个线程在访问,那就干脆把锁"偏向"这个线程好了。
当一个线程第一次获得锁时,JVM就把这个线程的ID记录在对象头里。以后这个线程再进入同步块时,只需要检查一下对象头里的线程ID是不是自己就行了,连CAS操作都不需要,这样性能就很好了。

public class Example {private final Object lock = new Object();public void doWork() {synchronized (lock) {// 如果只有一个线程反复调用这个方法// 第一次会设置偏向锁,后面进入几乎没有开销}}
}

但是偏向锁也有失效的时候。如果来了其他线程想要获取锁,偏向锁就得撤销了,这个过程还是有一定开销的。

轻量级锁

当偏向锁撤销后,或者有多个线程轮流访问同步块时,就该轻量级锁上场了。轻量级锁的核心思想是用CAS操作来替代重量级的互斥量。
轻量级锁的工作过程是这样的:线程要获取锁时,先在自己的栈帧中建立一个叫锁记录的空间,然后用CAS操作尝试把对象头的内容复制到这个锁记录中,同时把对象头更新成指向锁记录的指针。如果CAS成功了,就说明获得了锁;如果失败了,可能是其他线程已经获得了锁。

public class Example {private final Object lock = new Object();public void method1() {synchronized (lock) {// 线程A执行}}public void method2() {synchronized (lock) {// 线程B稍后执行,两个线程错开时间访问// 这种情况适合使用轻量级锁}}
}

自旋的优化策略

如果CAS失败了,线程并不会马上阻塞,而是会自旋一段时间,也就是在那里空转等待,看看锁会不会很快被释放。这是基于一个假设:大部分同步块的执行时间都很短,与其阻塞线程再唤醒,不如让线程等一等,因为如果阻塞时间很短,那么就让当前线程自旋,这是自旋性能代价小;但是如果阻塞时间很长,那不要一直自旋,直接阻塞线程就好了。
不过自旋也不能无限进行下去,如果自旋了一定次数还没有获得锁,就说明竞争比较激烈,这时候就会升级到重量级锁。

重量级锁

当自旋也解决不了问题,或者有很多线程同时竞争锁时,就只能使用重量级锁了。这时候JVM会使用操作系统提供的互斥量,没有获得锁的线程会被阻塞,需要等待操作系统来调度唤醒。
重量级锁虽然开销大,但是在高竞争的场景下反而是最合适的选择。因为如果竞争很激烈,让线程自旋反而会浪费CPU资源,还不如直接阻塞,等有机会再唤醒。

public class Example {private final Object lock = new Object();public void highContentionMethod() {synchronized (lock) {// 如果有很多线程同时竞争这个锁// 最终会升级为重量级锁try {Thread.sleep(100); // 模拟长时间操作} catch (InterruptedException e) {Thread.currentThread().interrupt();}}}
}

锁升级带来的实际价值

锁升级机制的最大好处就是能够根据实际情况选择最合适的锁实现。在没有竞争或竞争很少的情况下,偏向锁和轻量级锁的性能要比重量级锁好很多。据统计,在很多应用中,大部分synchronized代码块都没有多线程竞争,这种情况下锁升级能够显著提高性能。

http://www.dtcms.com/a/287902.html

相关文章:

  • 100条常用SQL语句
  • Spring AI 1.0版本 + 千问大模型之文本对话
  • ReentrantLock和synchronized的区别
  • 第二阶段-第二章—8天Python从入门到精通【itheima】-133节(SQL——DQL——基础查询)
  • 解决Maven版本不兼容问题的终极方案
  • 操作系统1.1.1+1.1.2:操作系统的概念、功能
  • 软考高级之工程工期成本计算题
  • 神经网络:从模式组合到多层神经网络的进化
  • 自由学习记录(70)
  • Java程序猿搬砖笔记(十九)
  • 零基础 “入坑” Java--- 十二、抽象类和接口
  • 从五次方程到计算机:数学抽象如何塑造现代计算
  • 大数据之路:阿里巴巴大数据实践——日志采集与数据同步
  • 网络爬虫概念初解
  • Rust Web 全栈开发(九):增加教师管理功能
  • 对话访谈 | 盘古信息×锐明科技:中国企业高质量出海“走进去”和“走上去”
  • 实验室危险品智能管控:行为识别算法降低爆炸风险
  • 配置华为交换机接口链路聚合-支持服务器多网卡Bind
  • element ui 表格懒加载操作问题
  • 最终分配算法【论文材料】
  • OpenCV 官翻6 - Computational Photography
  • 市场数据+幸存者偏差提问,有趣的思考?
  • 基于dcmtk的dicom工具 第六章 StoreSCU 图像发送
  • 研究的艺术
  • simulink系列之模型接口表生成及自动连线脚
  • 图 —— 拓扑排序➕Bitset!
  • XSS原型与原型链
  • Linux 常用命令详解(含目录结构 / 文件操作 / 查找 / 解压缩)- 新手入门教程
  • 接口测试工具
  • PDF发票批量打印工具哪个好?高效打印发票的实用工具推荐