JUC:synchronized优化——锁的升级过程(偏向锁->轻量级锁->重量级锁)以及内部实现原理

news2024/11/23 11:27:49

文章目录

  • 锁的类型
    • 轻量级锁
    • 重量级锁
    • 自旋优化
    • 偏向锁
    • 偏向锁的细节
      • 偏向锁的撤销
      • 批量重偏向
      • 批量撤销
      • 锁消除

锁的类型

重量级锁、轻量级锁、偏向锁。
加锁过程:偏向->轻量级->重量级

轻量级锁

轻量级锁的使用场景:如果一个对象虽然有多线程要加锁,但加锁的时间是错开的(也就是没有竞争),那么可以使用轻量级锁来优化。

轻量级锁对使用者是透明的,即语法仍然是 synchronized,锁的升级什么的不用我们考虑。

static final Object obj = new Object();

public static void method1() {
    synchronized( obj ) {
        // 同步块 A
        method2();
    }
}

public static void method2() {
    synchronized( obj ) {
        // 同步块 B
    }
}

每一个线程的栈帧中都会包含一个锁记录结构,记录对象头中的markword。

在这里插入图片描述

锁记录中Object reference指向锁对象,并尝试用 cas 交换 Object 的 Mark Word,将Mark Word的值存入锁记录中。

在这里插入图片描述

若cas成功,对象头中存储锁地址和锁状态00(轻量级),表示该线程给此对象加锁。

在这里插入图片描述

如果 cas 失败,有两种情况:

  • 如果是其它线程已经持有了该 Object 的轻量级锁,这时表明有竞争,进入锁膨胀过程。
  • 如果是自己执行了 synchronized 锁重入,那么再添加一条 Lock Record 作为重入的计数。

在这里插入图片描述

当退出 synchronized 代码块(解锁时)如果有取值为 null 的锁记录,表示有重入,这时重置锁记录,表示重入计数减一。

在这里插入图片描述

当退出 synchronized 代码块(解锁时)锁记录的值不为 null,这时使用 cas 将 Mark Word 的值恢复给对象头

  • 成功,则解锁成功。
  • 失败,说明轻量级锁进行了锁膨胀或已经升级为重量级锁,进入重量级锁解锁流程。

重量级锁

如果在尝试加轻量级锁的过程中,CAS操作无法成功,这时一种情况就是有其它线程为此对象加上了轻量级锁(有竞争),这时会进行锁膨胀,将轻量级锁膨胀为重量级锁。

static Object obj = new Object();
public static void method1() {
    synchronized( obj ) {
        // 同步块
    }
}

当 Thread-1 进行轻量级加锁时,Thread-0 已经对该对象加了轻量级锁。

在这里插入图片描述

这时Thread-1就会CAS失败,进入锁膨胀过程。

  • Object 对象申请 Monitor 锁,让 Object 指向重量级锁地址
  • 然后自己进入 Monitor 的 EntryList BLOCKED
  • Object 对象头锁标记变为10(重量级)

在这里插入图片描述

当 Thread-0 退出同步块解锁时,使用 CAS 将 Mark Word 的值恢复给对象头,失败。这时会进入重量级解锁流程,即按照 Monitor 地址找到 Monitor 对象,设置 Owner 为 null,唤醒 EntryList 中 BLOCKED 线程。

简单来说,我们可以简化一下:

  • 就是没竞争时,自己正常加锁运行解锁即可,同一线程有锁的重入,防止没必要的加锁解锁过程。
  • 如果有竞争时,新线程进入阻塞队列等待,当解锁时,唤醒阻塞队列中的线程。

自旋优化

重量级锁竞争的时候,可以使用自旋(不断的循环尝试获取重量级锁)来进行优化,如果当前线程自旋成功(当前持有锁的线程释放了锁),这时当前线程就可以避免阻塞。(阻塞再恢复,会进行上下文切换,耗费性能)

但是自旋也是会消耗性能的,尤其是进程非常多的时候,一堆进程在不断循环等待锁被释放,优点就是及时,一释放锁就能感应到。

偏向锁

在锁重入时,会产生锁记录,每次都需要尝试CAS,虽然会失败,但是CAS这个操作是没必要的。

优化:

将对象头中记录持有该锁的线程id,如果发生锁的重入,检测对象头,若是自己的,那么就不用再CAS了。

对比:

在这里插入图片描述

在这里插入图片描述

偏向锁的细节

在这里插入图片描述

一个对象创建时:

  • 如果开启了偏向锁(默认开启),那么对象创建后,markword 值为 0x05 即最后 3 位为 101,这时它的 thread、epoch、age 都为 0。

  • 偏向锁是默认是延迟的,不会在程序启动时立即生效,如果想避免延迟,可以加 VM 参数 -XX:BiasedLockingStartupDelay=0 来禁用延迟。

  • 如果没有开启偏向锁,那么对象创建后,markword 值为 0x01 即最后 3 位为 001,这时它的 hashcode、age 都为 0,第一次用到 hashcode 时才会赋值。

  • 正常状态对象一开始是没有 hashCode 的,第一次调用才生成。

  • 调用了 hashCode() 后会撤销该对象的偏向锁。

从图中也可以看出,偏向锁的markword没有位置可以放hashcode了,除非转变为轻量级锁,所以调用对象的hashCode会撤销他的偏向锁。

偏向锁的撤销

除了上面说的调用hashCode,也可用 让其他线程使用偏向锁,这时偏向锁会升级为普通锁。

不过两个线程需要错开加锁,不然会升级为重量级锁。

批量重偏向

可用发现,就算错开没用竞争,那么锁也会从偏向锁转变为普通锁,那其实锁的转变也是消耗性能的,所以就有了偏向锁的重偏向这个功能。

如果对象被多个线程访问,但没有竞争(错开),这时偏向了线程 T1 的对象仍有机会重新偏向 T2,重偏向会重置对象的 Thread ID 为 T2线程。

当某对象(这一类的不同对象)撤销偏向锁阈值超过20次后,jvm会觉得,我是不是偏向错了,于是会在给这些对象加锁的时候重新偏向至新加锁线程。

批量撤销

当相同类型不同对象撤销偏向锁阈值超过 40 次后,jvm 会这样觉得,自己确实偏向错了,根本就不该偏向,这个锁竞争太激烈了。于是整个类的所有对象都会变为不可偏向的,新建的该类型对象也是不可偏向的(轻量级)。

锁消除

简单来说,当jvm调用某方法达到一定阈值,就会用记时编译器优化,发现锁加和不加没有影响时,就会去除掉锁。

public class MyBenchmark {
    static int x = 0;
   
    public void a() throws Exception {
        x++;
    }
    
    public void b() throws Exception {
        Object o = new Object();
        synchronized (o) {
            x++;
        }
    }
}

比如Object为局部变量,根本不会逃离此方法作用范围,不能被共享,那加不加锁没啥意义,jvm就会去除掉锁去运行,从而优化性能。

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

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

相关文章

OpenHarmony Neptune开发板-MQTT连接华为IoT平台

本示例将演示如何在Neptune开发板上使用MQTT协议连接华为IoT平台,使用的是ATH20温湿度传感器模块与Neptune开发板 本示例实现AHT20温湿度数据上报华为IoT平台,IoT平台下发命令控制LED灯的开关 使用W800 SDK功能包中libemqtt来实现连接华为IoT平台 程序设计 初始化 一、MQT…

k8s 基础入门

1.namespace k8s中的namespace和docker中namespace是两码事,可以理解为k8s中的namespace是为了多租户,dockers中的namespace是为了网络、资源等隔离 2.deployment kubectl create #新建 kubectl aply #新建 更新 升级: 滚动升级&#x…

Find a way (DFS BFS)

//新生训练 #include <iostream> #include <algorithm> #include <queue> using namespace std; using PII pair<int, int>;const int N 205; const int inf 0x3f3f3f3f; int n, m; int l;int dis1[N][N]; int dis2[N][N];int dx[] {0, 0, -1, 1};…

HarmonyOS入门--页面和自定义组件生命周期

文章目录 页面和自定义组件生命周期页面生命周期组件生命周期生命周期的调用时机 页面和自定义组件生命周期 生命周期流程如下图所示&#xff0c;下图展示的是被Entry装饰的组件&#xff08;首页&#xff09;生命周期。 自定义组件和页面的关系&#xff1a; 自定义组件&…

zabbix绑定钉钉进行通知,网页端添加JavaScript,无脑式操作

文章目录 前言一、编辑zabbix告警JavaScript脚本二、代码如下:编辑消息模板,自定义markdown格式的消息。总结前言 随着人工智能的不断发展,zabbix监控这门技术也越来越重要,一下进入正题。 一、编辑zabbix告警JavaScript脚本 没有没接可以新增媒介 其中URL是你的机器人地…

Linux系统——网络管理

此文章以红帽Linux9版本为例进行讲解。 红帽Linux9版本的网络管理十分全面&#xff0c;可在多处进行网络配置的修改&#xff0c;但需要注意的是&#xff0c;在9版本内&#xff0c;用户可在配置文件内进行网络配置的修改&#xff0c;但系统不会执行修改的命令&#xff0c;而在9之…

redis之主从复制、哨兵模式

一 redis群集有三种模式 主从复制&#xff1a; 主从复制是高可用Redis的基础&#xff0c;哨兵和集群都是在主从复制基础上实现高可用的。 主从复制主要实现了数据的多机备份&#xff0c;以及对于读操作的负载均衡和简单的故障恢复。 缺陷&#xff1a; 故障恢复无法自动化&…

西圣、飞利浦、万魔开放式耳机好用吗?热门产品硬核测评对比

在无线耳机市场中&#xff0c;开放式耳机因其独特的佩戴方式和出色的听音体验逐渐受到消费者的青睐&#xff0c;西圣、飞利浦、万魔作为业内知名品牌&#xff0c;都推出了各自的开放式耳机产品&#xff0c;备受关注&#xff0c;那么这些热门品牌的开放式耳机究竟好用吗&#xf…

力扣热门算法题 174. 地下城游戏,189. 轮转数组,198. 打家劫舍

174. 地下城游戏&#xff0c;189. 轮转数组&#xff0c;198. 打家劫舍&#xff0c;每题做详细思路梳理&#xff0c;配套Python&Java双语代码&#xff0c; 2024.03.31 可通过leetcode所有测试用例。 目录 174. 地下城游戏 解题思路 完整代码 Python Java 189. 轮转数…

【Web应用技术基础】JavaScript(1)——案例:猜数字

上一个博客发了视频。这个博客因为不能插入视频&#xff0c;所以给大家一张一张截图的 点击“重新开始一局游戏” <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"…

[leetcode]剑指 Offer 29. 顺时针打印矩阵

前言&#xff1a;剑指offer刷题系列 问题&#xff1a; 输入一个矩阵&#xff0c;按照从外向里以顺时针的顺序依次打印出每一个数字。 示例&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;[1,2,3,6,9,8,7,4,5]思路&#xff1a; 看到这个…

Lambda表达式,Stream流

文章目录 Lambda表达式作用前提函数式接口特点 语法省略模式和匿名对象类的区别 Stream流思想作用三类方法获取方法单列集合(Collection[List,Set双列集合Map(不能直接获取)数组同一类型元素(Stream中的静态方法) 常见的中间方法终结方法收集方法 Optional类 Lambda表达式 作用…

客户案例丨拓数派向量计算引擎PieCloudVector助力东吴证券AIGC应用升级

1.项目背景 随着人工智能技术的不断创新和应用&#xff0c;我们可以看到人工智能在各个领域的应用越来越广泛。深度学习技术在图像识别、语音识别、自然语言处理等领域表现出色。机器学习算法的改进将解决更多实际问题&#xff0c;如增强学习、迁移学习和联合学习等&#xff…

一个瓶盖和电子工程师以及机械工程师能有什么联系?

过小的产品空间是一种罪 我也曾经是喜欢将东西做的小巧&#xff0c;其实这是不归路&#xff0c;特别是对于产品&#xff0c;一不小心&#xff0c;就会发生可怕的问题. 最近某厂供应的一批一体机&#xff0c;设计上有严重缺陷&#xff0c;外壳做小了&#xff0c;散热孔开的太小…

什么是智慧驿站?智慧驿站有哪些功能?创新型智慧公厕解说

近年来&#xff0c;随着智能科技的迅速发展&#xff0c;人们对于城市生活的期望也逐渐提升。作为城市基础设施的一部分&#xff0c;智慧驿站应运而生。它不仅是一座智慧公厕&#xff0c;更是集合了多种功能&#xff0c;给我们带来全新的城市生活体验。本文以智慧驿站智慧公厕源…

使用Vite创建一个vue3项目

一、vite是什么&#xff1f; vite 是一种新型前端构建工具&#xff0c;能够显著提升前端开发体验。它主要由两部分组成&#xff1a; 一个开发服务器&#xff0c;它基于原生 ES 模块提供了丰富的内建功能&#xff0c;如速度快到惊人的模块热更新&#xff08;HMR&#xff09;。 …

如何监控特权帐户,保护敏感数据

IT基础设施的增长导致员工可以访问的凭据和资源数量急剧增加。每个组织都存储关键信息&#xff0c;这些信息构成了做出关键业务决策的基石。与特权用户共享这些数据可以授予他们访问普通员工没有的凭据的权限。如果特权帐户凭证落入不法分子之手&#xff0c;它们可能被滥用&…

【随笔】Git 高级篇 -- 分离 HEAD(十一)

&#x1f48c; 所属专栏&#xff1a;【Git】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#x1f496; 欢迎大…

成都直播基地出租:天府新区兴隆湖天府锋巢直播产业基地

天府新区兴隆湖天府锋巢直播产业基地&#xff0c;作为成都乃至西部地区的一颗璀璨明珠&#xff0c;正以其独特的魅力和无限的潜力&#xff0c;吸引着越来越多的目光。这里不仅是成都直播产业的聚集地&#xff0c;更是传统企业转型升级的摇篮&#xff0c;是新媒体时代下的创新高…

v-text 和v-html

接下来&#xff0c;我讲介绍一下v-text和v-html的使用方式以及它们之间的区别。 使用方法 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-widt…