【JUC进阶】09. 关于锁升级

这篇具有很好参考价值的文章主要介绍了【JUC进阶】09. 关于锁升级。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

1、前言

2、回顾

2.1、对象头和内存布局

2.2、四大锁回顾

3、状态转换

3.1、锁状态

3.1.1、无锁状态

3.1.2、偏向锁状态

3.1.3、轻量级锁状态

3.1.4、重量级锁状态

3.2、状态转换条件

3.2.1、无锁 -> 偏向锁

3.2.2、偏向锁 -> 无锁

3.2.3、偏向锁 -> 轻量级锁

3.2.4、轻量级锁 -> 重量级锁

3.2.5、重量级锁 -> 轻量级锁

4、锁升级过程

5、锁是否可以降级?


1、前言

在并发编程中,锁是保证线程安全的重要机制。然而,传统的锁在高并发场景下性能可能受到限制。为了解决这个问题,JUC引入了锁升级的概念,通过在运行时动态调整锁的状态,提升并发性能。前面我们分别介绍了无锁,偏向锁,轻量级锁,自旋锁,重量级锁的知识。这些其实就是JUC中对锁的优化而会转换的几种状态,也就是我们经常听到的锁升级。

2、回顾

2.1、对象头和内存布局

对这一块知识还不太理解的,可以翻看《【JUC进阶】03. Java对象头和内存布局》。这边简单回顾一下。

Java对象在堆内存中存储的布局可以划分为三个部分:对象头(Header)、实例数据(Instance Data)、对齐填充(Padding)。而对象头(Header)中包含了两部分信息:标记字段(Mark Word)和Class对象指针(Class Pointer)。

其中,标记字段(Mark Word)用于存储对象自身的运行时数据,如HashCode(哈希码)、GC分代年龄,锁状态标志,线程持有的锁,偏向线程ID,偏向时间戳等。Class对象指针(Class Pointer)则是对象指向它的类型元数据的指针,JVM通过这个指针来确定该对象是哪个类的实例。

当对象持有各种级别锁的时候,标记字段(Mark Word)中会存储相关的标志位,线程ID等信息。

【JUC进阶】09. 关于锁升级,JUC进阶,开发语言,java

2.2、四大锁回顾

前面我们分别详细介绍了几种锁的知识。这里将几种锁的相关特性进行汇总。

锁类型

特性

本质

原理

优点

缺点

使用场景

性能开销

无锁

无阻塞,无同步

通过CAS实现原子操作

使用原子操作实现并发控制

无阻塞,避免线程阻塞和切换的开销

自旋等待消耗CPU资源

并发度高,争用少的情况

较低,仅涉及原子操作的性能损耗

偏向锁

适用于单线程

通过线程ID标识持有者

初次获取锁时,将线程ID记录在锁对象的Mark Word

避免了多线程竞争,加速单线程执行路径

多线程竞争时会撤销偏向锁,引入额外开销

频繁获取锁的单线程

较低,只涉及线程ID的比较和写操作

轻量级锁

自旋等待

通过CAS和自旋实现

偏向锁撤销或多线程竞争时,使用CAS将Mark Word 替换

减少了线程阻塞和切换的开销,适用于短时间的锁竞争

自旋等待消耗CPU资源

短时间的锁竞争

中等,涉及CAS操作和自旋等待

重量级锁

阻塞

使用操作系统Mutex

线程竞争激烈时,使用操作系统提供的互斥机制

可以有效解决多线程竞争,保证数据的安全和正确性

需要进行线程阻塞和切换,开销较大

长时间的锁竞争,保证数据的安全和正确性

高,涉及线程阻塞、切换和操作系统调度

可见,相对性能开销而言:无锁 ≤ 偏向锁 ≤ 轻量级锁 ≤ 重量级锁。如果有一定编程经验的朋友,一定会有这样的意识,升级过程必然会影响性能的开销,所以按照性能开销的分布是否可以推导出锁升级(状态转换)的过程。答案是必然的。

3、状态转换

3.1、锁状态

3.1.1、无锁状态

在无锁状态下,线程可以自由地访问共享资源,没有任何锁的限制和竞争。当多个线程同时访问同一个共享资源时,会发生数据竞争和线程安全问题。

3.1.2、偏向锁状态

当只有一个线程访问同步代码块时,JVM会将对象标记为偏向锁状态。偏向锁的目的是减少无竞争情况下的锁开销。当线程第一次进入同步代码块时,JVM会将对象头中的线程ID记录为当前线程的ID,并将对象头的状态设置为偏向锁。之后,该线程再次进入同步代码块时,无需进行额外的同步操作,直接进入同步状态。

3.1.3、轻量级锁状态

当多个线程之间存在轻度竞争时,JVM会将对象标记为轻量级锁状态。轻量级锁的目的是在减少线程切换和锁撤销开销的前提下,提供一种低竞争的同步机制。

3.1.4、重量级锁状态

当多个线程之间存在激烈竞争时,JVM会将对象标记为重量级锁状态。重量级锁使用操作系统提供的互斥量实现,涉及到线程的阻塞和唤醒,需要操作系统的介入。

3.2、状态转换条件

3.2.1、无锁 -> 偏向锁

  • 当一个线程第一次访问同步代码块时,对象会被标记为偏向锁状态,并记录当前线程的ID。
  • 转换条件:无锁状态下的对象被另一个线程访问。

3.2.2、偏向锁 -> 无锁

  • 当对象处于偏向锁状态时,如果另一个线程尝试获取锁,偏向锁会被撤销。
  • 转换条件:另一个线程尝试获取偏向锁。

3.2.3、偏向锁 -> 轻量级锁

  • 当一个线程反复进入同步代码块,但存在竞争时,偏向锁会升级为轻量级锁。
  • 转换条件:同一个对象上的偏向锁存在竞争。

3.2.4、轻量级锁 -> 重量级锁

  • 当多个线程之间存在激烈竞争时,轻量级锁会升级为重量级锁。
  • 转换条件:轻量级锁的CAS操作竞争失败。

3.2.5、重量级锁 -> 轻量级锁

  • 当持有重量级锁的线程释放锁时,锁会尝试降级为轻量级锁。
  • 转换条件:持有重量级锁的线程释放锁。

4、锁升级过程

无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁
  1. 偏向锁升级:当一个线程访问同步块时,首先会尝试获取偏向锁。如果当前对象没有被其他线程竞争过,并且持有偏向锁的线程仍然存活,那么当前线程可以直接获取偏向锁,不会发生锁升级。
  2. 轻量级锁升级:如果获取偏向锁失败,表示当前对象存在竞争,那么偏向锁会升级为轻量级锁。这时,JVM会通过CAS操作将对象头中的锁标记改为指向线程栈中的锁记录(Lock Record)的指针,并将对象的内容复制到锁记录中。
  3. 自旋锁升级:如果轻量级锁获取失败,即有多个线程竞争同一个对象的锁,那么轻量级锁会升级为自旋锁。自旋锁不会使线程阻塞,而是让线程执行忙等待,尝试反复获取锁。这样可以避免线程切换带来的性能损失。
  4. 重量级锁升级:当自旋锁尝试获取锁的次数达到一定阈值,或者等待时间超过一定限制时,自旋锁会升级为重量级锁。重量级锁会使线程阻塞,将竞争锁的线程放入等待队列,等待锁释放后进行唤醒。

大体的升级流程图:

【JUC进阶】09. 关于锁升级,JUC进阶,开发语言,java

具体流程图如下:

【JUC进阶】09. 关于锁升级,JUC进阶,开发语言,java

5、锁是否可以降级?

在Java中,锁通常不会主动降级,也就是说,一旦锁升级到了更高级别的锁(如从偏向锁升级到轻量级锁或重量级锁),就不会再自动降级回低级别的锁。

然而,有一种情况下锁会出现降级的行为,即重量级锁在释放时可以降级为轻量级锁。这种降级发生在持有重量级锁的线程释放锁之后,如果接下来的竞争情况较为温和,即锁的争用程度较低,系统会尝试将重量级锁降级为轻量级锁,以减少后续线程竞争锁时的开销。

降级的过程是由JVM自动处理的,具体的触发条件和策略可能因JVM实现而有所不同。一般来说,当释放重量级锁的线程检测到没有其他线程争用同一个锁时,会将锁降级为轻量级锁。

需要注意的是,锁的降级并非在所有情况下都发生,它依赖于系统的竞争情况和JVM的具体实现。在实际应用中,我们无法直接控制锁的降级行为,因此在选择和使用锁时,应根据具体情况和需求综合考虑,权衡锁的级别和性能。

 文章来源地址https://www.toymoban.com/news/detail-531642.html

到了这里,关于【JUC进阶】09. 关于锁升级的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 多线程(进阶三:JUC)

    目录 一、Callable接口 1、创建线程的操作 2、编写多线程代码 (1)实现Runnable接口(使用匿名内部类) (2)实现Callable接口(使用匿名内部类) 二、ReentrantLock 1、ReentrantLock和synchronized的区别 2、如何选择使用哪个锁? 三、原子类 四、线程池 五、信号量 Semaphore 代码示例 六、

    2024年02月20日
    浏览(33)
  • 【JUC进阶】13. InheritableThreadLocal

    目录 1、前言 2、回顾ThreadLocal 3、InheritableThreadLocal 4、实现原理 5、线程池中的问题 6、小结 在《【JUC基础】14. ThreadLocal》一文中,介绍了ThreadLocal主要是用于每个线程持有的独立变量。通俗的说就是ThreadLocal是每个线程独有的一份内存,且各个线程间是独立、隔离的。但是随

    2024年01月25日
    浏览(29)
  • 多线程JUC 第2季 synchronized锁升级过程

    用锁能够实现数据的安全,但是会带来性能下降。Synchronized是一个重量级锁,锁的升级过程: 无锁-偏向锁-轻量级锁-重量级锁。 高并发时,同步调用应尽量考虑锁的性能损耗,能用无锁数据结构,就不要用锁,能用区块不要用锁住整个方法体;能有对象锁,就不要用类锁。

    2024年02月09日
    浏览(40)
  • 前端进阶Html+css09----BFC模型

    1.什么是BFC模型 全称是:Block formatting context(块级格式化上下文),是一个独立的布局环境,不受外界的影响。 2.FC,BFC,IFC 元素在标准流里都属于一个FC(Formatting Context)。 块级元素的布局属于一个BFC(Block formatting context)。例如div/p/h1等 - BFC布局中盒子 行内级元素的布局属

    2024年02月11日
    浏览(49)
  • 【UE4 塔防游戏系列】09-防御塔升级、击杀敌人增加金钱

    目录 效果  步骤 一、控件蓝图文本控件内容绑定金钱数  二、防御塔改造 三、击杀敌人增加金钱  四、防御塔升级功能 一、控件蓝图文本控件内容绑定金钱数  1. 打开“TaFangGameMode”,新增一个变量命名为“PlayerMoney”,默认值设为20,用于表示玩家的金钱数。金钱可以用来

    2024年02月16日
    浏览(43)
  • 【区块链技术开发】 关于Windows10平台Solidity语言开发环境配置

    在 Windows 上配置 Solidity 语言开发环境需要进行以下步骤:

    2023年04月20日
    浏览(67)
  • 【Java 进阶篇】Java Web开发:实现验证码功能

    在Web应用程序中,验证码(CAPTCHA)是一种常见的安全工具,用于验证用户是否为人类而不是机器。验证码通常以图像形式呈现,要求用户在登录或注册时输入正确的字符。在这篇文章中,我们将详细介绍如何在Java Web应用程序中实现验证码功能。 验证码是“全自动区分计算机

    2024年02月03日
    浏览(40)
  • opencv进阶09-视频处理cv2.VideoCapture示例(打开本机电脑摄像头)

    视频信号(以下简称为视频)是非常重要的视觉信息来源,它是视觉处理过程中经常要处理的一类信号。实际上,视频是由一系列图像构成的,这一系列图像被称为帧,帧是以固定的时间间隔从视频中获取的。获取(播放)帧的速度称为帧速率,其单位通常使用“帧/秒”表示

    2024年02月12日
    浏览(54)
  • 【MySQL | 进阶篇】09、MySQL 管理及常用工具(mysqladmin、mysqlbinlog、mysqldump 等)的使用

    目录 一、系统数据库 二、常用工具  2.1 mysql 示例 2.2 mysqladmin  示例  2.3 mysqlbinlog 示例 2.4 mysqlshow 示例 2.5 mysqldump(数据备份) 示例 2.6 mysqlimport/source(数据恢复) 2.6.1 mysqlimport 2.6.2 source Mysql 数据库安装完成后,自带了以下四个数据库,具体作用如下: 该 mysql 不是指 my

    2023年04月21日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包