【JUC进阶】06. 轻量级锁

news2025/1/13 2:35:37

目录

1、前言

2、轻量级锁

2.1、什么是轻量级锁

2.2、工作原理

2.2.1、回顾Mark Word

2.2.2、工作流程

2.3、解锁

3、适用场景

4、注意事项

5、轻量级锁与偏向锁的对比

6、小结


1、前言

前面一节我们讲到了偏向锁。当偏向锁被撤销,或其他线程竞争的时候,偏向锁会撤销并且升级为轻量级锁。轻量级锁(Lightweight Lock)机制,它是一种介于偏向锁和重量级锁之间的锁实现。

2、轻量级锁

2.1、什么是轻量级锁

轻量级锁(Lightweight Lock)是JDK 6 时加人的新型锁机制,它名字中的“轻量级”是相对于使用操作系统互斥量来实现的传统锁而言的,因此传统的锁机制就被称为“重量级”锁。不过,需要强调一点,轻量级锁并不是用来代替重量级锁的,它设计的初衷是在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗。

在无竞争的情况下,轻量级锁使用CAS操作来实现锁的获取和释放,避免了线程的阻塞和唤醒,从而提高了并发性能。

2.2、工作原理

2.2.1、回顾Mark Word

关于HotSpot对象头的具体内容,可以查看《【JUC进阶】03. Java对象头和内存布局》文章。

这里需要注意的是:对于对象头的设计,考虑到JVM的空间使用效率,Mark Word被设计成一个非固定的动态数据结构,以便在极小的空间内存能够存储尽可能多的信息。如32位虚拟机中,对象未被锁定的状态下,有25bit用来存储对象hashcode,而当进入偏向模式后,存储hashcode的内存空间被用来存储线程ID和Epoch。

2.2.2、工作流程

  1. 初始状态:对象的对象头中的锁标志位为无锁状态。
  2. 获取锁:当线程尝试获取轻量级锁时,首先会将对象头中的锁标志位复制到线程的栈帧中的锁记录(Lock Record)中。
  3. CAS操作:线程使用CAS操作将对象头中的锁标志位替换为指向线程栈帧的指针。如果CAS操作成功,表示当前线程成功获取了锁,并进入临界区代码执行。如果CAS操作失败,说明存在竞争,进入下一步操作。

在代码即将进入同步块的时候,如果此同步对象没有被锁定(锁标志位为“01”状态),虚拟机首先将在当前线程的栈帧中建立一个名为锁记录 (Lock Record)的空间,用于存储锁对象目前的Mark Word的拷贝 (官方为这份拷贝加了一个 Displaced 前缀,即 Displaced MarkWord)。

线程堆栈与对象头状态:

然后,虚拟机将使用 CAS 操作尝试把对象的 Mark Word 更新为指向 Lock Record 的指针。如果这个更新动作成功了,即代表该线程拥有了这个对象的锁,并且对象Mark Word的锁标志位 (Mark Word 的最后两个比特)将转变为“00”,表示此对象处于轻量级锁定状态。

如果这个更新操作失败了,那就意味着至少存在一条线程与当前线程竞争获取该对象的锁。

那么,虚拟机首先会检查对象的 Mark Word 是否指向当前线程的栈帧,如果是,说明当前线程已经拥有了这个对象的锁,那直接进入同步块继续执行就可以了。否则就说明这个锁对象已经被其他线程抢占了。如果出现两条以上的线程争用同一个锁的情况,那轻量级锁就不再有效,必须要膨胀为重量级锁(关于锁膨胀,后面会有专门文章来聊聊),锁标志的状态值变为“10”,此时 Mark Word 中存储的就是指向重量级锁(互斥量)的指针,后面等待锁的线程也必须进入阻塞状态。

当CAS操作成功,对象处于轻量级锁状态下,线程堆栈与对象头的状态:

2.3、解锁

上面描述的是轻量级锁的加锁过程,它的解锁过程也同样是通过 CAS 操作来进行的,如果对象的 Mark Word 仍然指向线程的锁记录,那就用CAS操作把对与对象的状态象当前的Mark Word 和线程中复制的Displaced MarkWord 替换回来。假如能够成功替换,那整个同步过程就顺利完成了;如果替换失败,则说明有其他线程尝试过获取该锁,就要在释放锁的同时,唤醒被挂起的线程。

  1. 检查是否存在竞争:当线程要释放轻量级锁时,首先会检查自己是否存在竞争,即检查对象头中的锁标志位是否与线程栈帧中的锁记录相等。如果不相等,表示当前线程已经失去了锁的控制权,无法解锁,需要进行额外的处理。
  2. CAS操作释放锁:如果线程检查通过,说明当前线程仍然持有锁,可以使用CAS操作将对象头中的锁标志位恢复为无锁状态,表示锁已经释放。
  3. 自旋失败后恢复为重量级锁:如果CAS操作失败,即存在竞争,当前线程会将锁膨胀为重量级锁,进入阻塞状态。

3、适用场景

  1. 对象锁竞争不激烈:轻量级锁适用于多线程对同一对象进行频繁竞争的情况下。当竞争不激烈时,轻量级锁可以减少线程阻塞和唤醒的开销,提高程序的并发性能。
  2. 短时间的同步块:轻量级锁适用于同步块的执行时间较短的情况下。由于轻量级锁的获取和释放操作开销较小,适用于保护临界区代码执行时间较短的场景。
  3. 无锁或偏向锁失败:当对象处于无锁状态或偏向锁状态时,如果有其他线程尝试获取对象的锁,但偏向锁失败,那么会自动升级为轻量级锁。因此,轻量级锁适用于无锁或偏向锁的竞争情况下。

需要注意的是,轻量级锁并不适用于所有场景。当竞争激烈或同步块执行时间较长时,轻量级锁的性能可能不如重量级锁。此时,可以考虑使用其他同步机制,如重量级锁或并发集合类,以满足并发性能的需求。

代码示例:

public class LightweightLockDemo {
    private static int count = 0;

    public static void main(String[] args) {
        final LightweightLockDemo demo = new LightweightLockDemo();
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                for (int j = 0; j < 100000; j++) {
                    demo.increment();
                }
            }).start();
        }

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Count: " + count);
    }

    public synchronized void increment() {
        count++;
    }
}

上面的示例代码中,我们定义了一个LightweightLockDemo类,其中的increment()方法使用synchronized关键字来实现轻量级锁。在main()方法中,我们创建了5个线程,并通过调用increment()方法对count变量进行累加操作。每个线程都会多次调用increment()方法。最后,我们等待2秒钟,确保所有线程执行完毕后,打印出最终的count值。

4、注意事项

在使用轻量级锁时,需要注意以下事项和考虑优化的技巧:

  1. 减少锁的竞争:轻量级锁的性能取决于锁竞争的程度。因此,减少锁的竞争是提高轻量级锁性能的关键。可以通过减小同步块的粒度、减少锁的持有时间等方式来降低锁竞争。
  2. 避免锁的扩大:轻量级锁在竞争激烈的情况下可能会退化为重量级锁,这会增加线程的阻塞和唤醒开销。因此,需要尽量避免锁的扩大,即尽量减少锁的竞争范围,仅在必要时才使用锁。
  3. 注意锁的释放时机:轻量级锁的释放是通过线程解锁和CAS操作实现的。在释放锁之前,需要确保没有其他线程在竞争同一个锁。因此,在释放锁之前要仔细考虑同步块的执行流程,避免出现不正确的解锁操作。
  4. 合理设置线程优先级:线程的优先级可以影响轻量级锁的竞争情况。在高优先级线程和低优先级线程竞争同一个锁时,高优先级线程更有可能获取到轻量级锁。因此,根据实际需求和线程优先级的设置,合理调整锁的竞争情况。
  5. 使用适当的锁机制:轻量级锁适用于竞争不激烈、同步块执行时间较短的场景。如果竞争激烈或同步块执行时间较长,可以考虑使用其他锁机制,如重量级锁、读写锁等,以满足性能和并发性的需求。

5、轻量级锁与偏向锁的对比

  1. 竞争情况:轻量级锁和偏向锁都是为了减少锁竞争而设计的,但适用于不同的竞争情况。偏向锁适用于存在线程间交替获取锁的情况下,锁的竞争程度较低的场景。轻量级锁适用于锁的竞争程度较高的场景。
  2. 锁状态转换:偏向锁会经历无锁状态、偏向锁状态和轻量级锁状态三个状态的转换。当只有一个线程获取锁时,会进入偏向锁状态,无需加锁和解锁操作。当其他线程竞争同一个锁时,会将偏向锁升级为轻量级锁状态。而轻量级锁则会直接从无锁状态转变为轻量级锁状态。
  3. 锁的竞争程度:由于偏向锁是针对低竞争场景设计的,所以在竞争激烈的情况下,偏向锁的性能可能不如轻量级锁。轻量级锁采用CAS操作进行加锁和解锁,当多个线程竞争同一个锁时,会进行自旋操作,减少了线程的阻塞和唤醒开销。
  4. 锁的撤销:偏向锁在发生竞争时需要撤销偏向锁状态,回到无锁状态。而轻量级锁在发生竞争时会退化为重量级锁,需要使用互斥量进行加锁和解锁。因此,偏向锁的撤销操作开销较小,而轻量级锁的撤销操作开销较大。

偏向锁适用于线程间竞争较少的情况下,可以减少不必要的锁操作开销。

轻量级锁适用于锁竞争较多的情况下,通过自旋操作减少线程阻塞和唤醒的开销。

6、小结

轻量级锁能提升程序同步性能的依据是“对于绝大部分的锁,在整个同步周期内都是不存在竞争的”这一经验法则。如果没有竞争,轻量级锁便通过 CAS 操作成功避免了使用互斥量的开销;但如果确实存在锁竞争,除了互斥量的本身开销外,还额外发生了CAS操作的开销。因此在有竞争的情况下,轻量级锁反而会比传统的重量级锁更慢。

 

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

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

相关文章

国内脑机接口最新研究 - 脑控机器人在非结构化场景执行避障任务

&#x1f482; 个人主页: 同学来啦&#x1f91f; 版权: 本文由【同学来啦】原创、在CSDN首发、需要转载请联系博主 &#x1f4ac; 如果文章对你有帮助&#xff0c;欢迎关注、点赞、收藏和订阅专栏哦 文章目录 &#x1f31f; 一、能避障的脑控机器人导航&#x1f31f; 二、机器…

Moveit1 机器人配置包超详细解析

前言 Moveit的robot_moveit_config包里有很多文件&#xff0c;在配置时容易搞不清关系&#xff0c;导致配置过程艰难、漫长。 同时互联网上的文档几乎没有详细介绍这部分配置包的&#xff0c;大神各有各的配法&#xff0c;比较混乱。 因此笔者整理了配置包内各个重要的文件&…

Volvo EDI 项目 MySQL 方案开源介绍

近期为了帮助广大用户更好地使用 EDI 系统&#xff0c;我们根据以往的项目实施经验&#xff0c;将成熟的 EDI 项目进行开源。用户安装好知行之桥 EDI 系统之后&#xff0c;只需要下载我们整理好的示例代码&#xff0c;并放置在知行之桥指定的工作区中&#xff0c;即可开始使用。…

Win10安装SQLite Expert Pers Setup64

在win10系统下安装SQLite Expert&#xff0c;我的电脑是64位系统&#xff0c;所以选择SQLite Expert Pers Setup64 一、软件下载 1、官网地址 SQLite administration | SQLite Expert 点击“DOWNLOAD”&#xff0c;选择红框中的免费版本 2、百度网盘下载 链接&#xff1a;h…

SQL SERVER2008导入导入mdf和ldf文件

把需要添加的文件复制粘贴到这个路径下&#xff1a; 再回到这点击添加&#xff1a;

商品减库在Redis中的运用

一.商品减库中存在问题 1.传统的代码 1.1引入jar包 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.…

chatgpt赋能python:Python转换DAT的方法详解

Python转换DAT的方法详解 在数据处理的应用场景中&#xff0c;我们经常要将DAT文件格式转换为其他数据格式&#xff0c;比如CSV或Excel等。此时&#xff0c;Python的数据处理能力就表现出其强大的优势。在本文中&#xff0c;我们将详细介绍使用Python转换DAT文件格式的方法。 …

chatgpt赋能python:在Python中如何实现超链接本地文件

在Python中如何实现超链接本地文件 介绍 超链接是在网页中实现页面跳转的一种技术&#xff0c;而本地文件指的是计算机本地存储的文件。在Python中&#xff0c;我们可以通过一些库来实现超链接本地文件。 具体来说&#xff0c;需要使用到Python中的web框架以及文件操作库&am…

重温数据结构与算法之KMP算法

文章目录 前言一、原理1.1 暴力法1.2 最长公共前后缀 二、代码实现2.1 next数组2.2 可视化next2.3 KMP 三、总结3.1 优点3.2 缺点 参考 前言 ​ KMP 算法是一种字符串匹配算法&#xff0c;它可以在一个主串中查找一个模式串的出现位置。在实际应用中&#xff0c;字符串匹配是一…

LangChain 任意命令执行(CVE-2023-34541)

漏洞简介 LangChain是一个用于开发由语言模型驱动的应用程序的框架。 在LangChain受影响版本中&#xff0c;由于load_prompt函数加载提示文件时未对加载内容进行安全过滤&#xff0c;攻击者可通过构造包含恶意命令的提示文件&#xff0c;诱导用户加载该文件&#xff0c;即可造…

【数据结构与算法】4、双向链表(学习 jdk 的 LinkedList 部分源码)

目录 一、双向链表二、node(int index) 根据索引找节点三、clear()四、add(int, E&#xff09;五、remove(int index)六、双向链表和单链表七、双向链表和动态数组八、jdk 官方的 LinkedList 的 clear() 方法 一、双向链表 &#x1f381; 单链表的节点中只有一个 next 指针引用…

1754_C语言assert函数功能初探

全部学习汇总&#xff1a; GreyZhang/c_basic: little bits of c. (github.com) 最近学习的过程中遇到了C语言中的assert函数&#xff0c;弄不明白这个函数到底是什么用。简单查了一下总结内容如下&#xff1a; 首先&#xff0c;此函数的声明头文件在assert.h中&#xff0c;使…

数据结构之哈夫曼树和哈夫曼编码

切入正题之前&#xff0c;我们先了解几个概念&#xff1a; 路径&#xff1a;从树的一个结点到另一个结点分支所构成的路线路径长度&#xff1a;路径上的分支数目树的路径长度&#xff1a;从根结点出发到每个结点的路径长度之和带权路径长度&#xff1a;该结点到根结点的路径长…

Spring概念:容器、Ioc、DI

目录 什么是容器&#xff1f; 什么是 IoC&#xff1f; 传统程序的开发 理解 Spring IoC DI 总结 我们通常所说的 Spring 指的是 Spring Framework&#xff08;Spring 框架&#xff09;&#xff0c;它是⼀个开源框架&#xff0c;有着活跃⽽庞⼤的社区&#xff0c;这就是它…

从C语言到C++_22(继承)多继承与菱形继承+笔试选择题

目录 1. 继承 1.1 继承的概念 1.2 继承的定义格式 1.3 访问限定符和继承方式 1.4 继承中的赋值 1.5 继承中的作用域 2. 子类(派生类)的默认成员函数 2.1 子类的构造函数 2.2 子类的拷贝构造函数 2.3 子类的赋值重载 2.4 子类的析构函数 2.5 小总结 3. 继承与友元…

欧洲运输业的创新能力评估报告(英)(附下载)

5月&#xff0c;联合研究中心(JRC)在交通研究与创新监测与信息系统&#xff08;TRIMIS&#xff09;上发布了一份报告&#xff0c;提供了对欧盟运输部门创新能力的最新评估。TRIMIS通过欧盟层面的宏观指标分析&#xff0c;对运输创新能力进行定期评估。 该报告分析了研究与开发(…

Tuxera NTFS2023Mac电脑免费U盘硬盘读写工具

Mac用户在使用NTFS格式移动硬盘时&#xff0c;会遇到无法写入硬盘的情况。要想解决无法写入的问题&#xff0c;很多人选择使用Mac读写软件。面对市面上“众多”的读写硬盘软件&#xff0c;用户应该怎么选择呢&#xff1f;初次接触移动硬盘的伙伴可能不知道移动硬盘怎么和电脑连…

探析ModaHub魔搭社区中文文本生成图片AI模型的现状、趋势和未来发展方向

目录 一、现状分析 二、趋势分析 三、未来预测 ModaHub魔搭社区是一个专注于AI模型开发和分享的平台&#xff0c;其中文本生成图片AI模型是其中的一个重要领域。本文将通过对ModaHub魔搭社区中文文本生成图片AI模型排行榜的数据分析&#xff0c;来探讨该领域的现状、趋势和未…

Linux安装nginx 反向代理 负载均衡 动静分离 高可用等使用

随着软件需求的发展&#xff0c;现在很多的系统都需要保证高可用、高并发&#xff0c;在此需求之下就需要部署的服务能够不间断的提供服务即避免单点故障问题因此系统需要做集群部署同时还能提升qps、tps等指标&#xff1b;集群部署后的服务就需要对用户的请求能够负载均衡&…

12 通用同步异步收发器(USART)

目录 通用同步异步收发器&#xff08;USART&#xff09; 理论部分 USART概览 STM32和PC通信模型 STM32和PC通过RS-232标准通信 RS-232标准介绍 RS-232协议电平标准对比 RS-232标准的物理接口规定及接口标号 RS-232标准下接口标号的作用 RS-232标准数据传输协议层 协议…