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

news2024/9/20 18:35:32

目录

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等信息。

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. 重量级锁升级:当自旋锁尝试获取锁的次数达到一定阈值,或者等待时间超过一定限制时,自旋锁会升级为重量级锁。重量级锁会使线程阻塞,将竞争锁的线程放入等待队列,等待锁释放后进行唤醒。

大体的升级流程图:

具体流程图如下:

5、锁是否可以降级?

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

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

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

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

 

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

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

相关文章

SAP 查询字段所存在的表(增强定义)

T-CODE——axtshow DATA:lv_zzbranch TYPE ze_les_branch, ls_aet088 TYPE zlesaet088_bol_attr, lr_adminh TYPE REF TO cl_crm_bol_entity. lr_entity_relationv TYPE REF TO cl_crm_bol_entity. lr_entity_relationv lr_adminh->get_related_entity( …

15. 查询所有用户的连续登录两天及以上的日期区间

文章目录 题目需求思路一实现一题目来源 题目需求 从登录明细表(user_login_detail)中查询出,所有用户的连续登录两天及以上的日期区间,以登录时间(login_ts)为准。 期望结果如下: user_id (…

C#与C++编程环境对比:优点与应用场景详解

C#与C是两种常用的编程语言,它们在编程环境方面有一些相同点和不同点。首先,它们都可以用于开发跨平台的应用程序。其次,它们都具有强大的面向对象编程能力。但是,它们在语法、性能和应用领域等方面存在一些不同点 。 在语法方面…

编织魔法:探索Python字符串的奇幻世界

文章目录 前言什么是 python 字符串通过下标(索引)来访问字符串中的字符字符串的切片操作字符串常用的方法find() 查找子串index() 查找子串rfind() / rindex()count() 返回某个子串在字符串中出现的次数replace() 替换split() 按照指定字符分割字符串join() 用一个字符或者子串…

Debian 12 静态IP / 固定IP的设置

环境:Debian 12 amd64-lxde 局域网:PT925E电信光猫 手机APP 网络管家 一般用动态IP就可以了,但如果软件环境比较小众,问题就随之而来。起始问题:路由器无法解析设备名和IP,网络管家也不让设置固定IP&…

炫酷水球图:让数据可视化更生动有趣

水球图是一种独特而引人注目的数据可视化形式,通过模拟水球的形状和大小来展示数据的分布和比例关系。它以其生动的表现形式和直观的视觉效果,吸引了越来越多的关注和应用。在数据可视化领域,水球图为我们提供了一种直观、易于理解的方式来展…

实施供应商管理时常见的挑战及解决方案

供应商管理在现代商业环境中是很重要的,是企业在采购活动中的重要组成部分。采购组织必须确保其供应商得到妥善管理,以维持成功的工作关系。然而,管理好供应商是一项艰巨且具有挑战性的任务。 以下是采购组织常面临的挑战以及有效的解决方案。…

SNMP 计算机网络管理 实验1(二) 练习与使用Wireshark抓取SNMP数据包抓包之 任务三分析并验证TCP三次握手建立连接时三次握手工作过程

⬜⬜⬜ 🐰🟧🟨🟩🟦🟪(*^▽^*)欢迎光临 🟧🟨🟩🟦🟪🐰⬜⬜⬜ ✏️write in front✏️ 📝个人主页:陈丹宇jmu &am…

您应该设置 Kubernetes CPU 限制吗?

管理Pod 和容器可用的资源是 Kubernetes 管理的最佳实践步骤。您需要防止 Pod 贪婪地消耗集群的 CPU 和内存。一组 Pod 的过度使用可能会导致资源争用,从而减慢相邻容器的速度并破坏主机的稳定性。 但 Kubernetes 资源管理经常被误解。提供了两种机制来控制分配:请求和限制。…

打jar包

pom文件指定打包的类型是jar&#xff0c;并指定启动主类 <packaging>jar</packaging><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin</artifactId><version>…

django要在books接口中不同数据的新类

要在books接口中添加一个处理不同数据的新类&#xff0c;并希望通过 Django 管理后台添加和显示这些数据&#xff0c;需要对三个文件进行修改 1.在 models.py 文件中添加新的模型类。 例如&#xff0c;假设您要添加一个名为 Category 的新模型&#xff1a; from django.db i…

Mac菜单栏图标管理软件 Bartender 4

Bartender 4是一款用于管理 macOS 系统状态栏图标的软件。它允许你自定义状态栏图标的显示和排序&#xff0c;帮助你更好地组织和管理你在状态栏中使用的应用程序和工具。 通过 Bartender 4&#xff0c;你可以隐藏或显示状态栏图标&#xff0c;以避免状态栏过多的图标拥挤在一起…

dbeaver远程数据库连接管理工具使用,数据库拒绝访问,用python远程访问postgre数据库

dbeaver软件 安装postgresql sudo apt-get update sudo apt-get install postgresql postgresql-client 修改postgresql密码,创建数据库 psql -U postgres ALTER USER postgres WITH PASSWORD new_password; CREATE DATABASE DB_NAME; \q 允许数据库被远程访问 点insert,加…

解决VS2013 C#无法联网搜索NuGet程序包的问题

1问题描述 2 结果办法 先打开程序包管理控制台 在程序包控制台输入后按回车&#xff1a;[Net.ServicePointManager]::SecurityProtocol[Net.ServicePointManager]::SecurityProtocol-bOR [Net.SecurityProtocolType]::Tls12 完成

7.1.6 Linux 文件系统的运行

所有的数据都得要载入到内存后CPU才能够对该数据进行处理。为了解决耗时等待磁盘的写入/读取上的问题&#xff0c;Linux使用的方式是通过一个称为非同步处理&#xff08;asynchronously&#xff09; 的方式。所谓的非同步处理是这样的&#xff1a; 当系统载入一个文件到内存后…

SAP ABAP结构复用的两种数据结构

一、定义结构中嵌套一层结构&#xff1a; 断点内表看结构是这样的&#xff1a; 二、定义结构中字段都放平层&#xff0c;注意那个include关键字的颜色&#xff0c;要变蓝色&#xff1a; 断点看内表结构&#xff0c;对了&#xff0c;就是我们想要的一层&#xff1a;

Oracle 查询下载补丁集

想必许多朋友在打开oracle支持网站后有些懵不知道如何查询下载数据库的补丁。 登入 登入Oracle support网站&#xff1a; https://support.oracle.com/portal/ 查询文档编号&#xff1a;2118136.2 选择需要的补丁编号&#xff1a; 选择对应的版本&#xff1a; 提前阅读…

java项目之疫情期间医院门诊管理系统ssm源码+文档

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于ssm的疫情期间医院门诊管理系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 &#x1f495;&#x1f495;作者&#xff1a;风…

java fastdfs实现文件(图片)的上传下载

1、首先引入依赖、目录结构&#xff1a; <!--fastdfs--> <dependency><groupId>com.github.tobato</groupId><artifactId>fastdfs-client</artifactId><version>1.26.5</version> </dependency>2、在application.yml中…

redis与分布式

主从复制 概念 主从复制&#xff0c;是指将一台Redis服务器的数据&#xff0c;复制到其他的Redis服务器。前者称为主节点(Master)&#xff0c;后者称为从节点(Slave)&#xff0c;数据的复制是单向的&#xff0c;只能由主节点到从节点。Master以写为主&#xff0c;Slave 以读为…