Synchronized 的锁升级过程介绍(无锁 --> 偏向锁 --> 轻量级锁 --> 重量级锁 )

本文主要是介绍Synchronized 的锁升级过程介绍(无锁 --> 偏向锁 --> 轻量级锁 --> 重量级锁 ),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • Synchronized 的锁升级过程
    • 1、什么是锁
      • 1-1:JVM理解:
      • 1-2:对象头:
      • 1-3:synchronized 线程演示数字累加
        • 1-3-1:没加锁测试:
        • 1-3-2:加 synchronized 锁测试:
    • 2、Synchronized 的锁升级过程
      • 锁为什么要升级?
      • 锁的四种实现(状态)
        • 1:无锁
          • 1-1:无竞争的情况
          • 2-1:存在竞争情况,非锁方式同步线程
        • 2:偏向锁
        • 3:轻量级锁
        • 4:重量级锁
    • 3、如何查看锁的状态
    • 4、锁修饰对象和方法的代码
    • 5、Synchronized 锁的升级总结

Synchronized 的锁升级过程


1、什么是锁

在并发环境下,多个线程会对同一个资源进行争抢,一些增加或修改数据的操作,就有可能导致数据不一致的问题,为了解决这个问题,所以很多编程语言都引入了锁机制。
通过一种抽象的锁来对资源进行锁定。


1-1:JVM理解:


如图:JVM 运行时内存结构主要包括这些:
JVM 详解


线程私有区域 : 程序计数器、虚拟机栈、本地方法栈;对于这个区域中的数据,不会出现线程竞争的问题。
虚拟机栈:存放方法、局部变量、方法参数


线程共享区域: Java堆、方法区;这个区域因为是所有线程共享的,所以就存在多个线程同时竞争这些数据,导致出现一些数据一致性的问题。因此需要锁机制对其进行限制。

Java堆:存放对象

方法区:存放类信息、常量

在这里插入图片描述


1-2:对象头:

“锁”是一种抽象概念,那么在代码层面是如何实现的?

在Java中,每个Object,就是每个对象,都拥有一把锁,这把锁存放在对象头中,锁中记录了当前对象被哪个线程所占用。
在这里插入图片描述


Java对象和对象头的关系:

Java 对象包含3部分内容:对象头、实例数据、填充字节。

在这里插入图片描述


对象头中的Mark word:如图:
在这里插入图片描述


1-3:synchronized 线程演示数字累加

synchronized 作用是使线程同步
在这里插入图片描述


1-3-1:没加锁测试:

在这里插入图片描述


num 从 0 开始累加,最终结果应该是 1999 ,这里明显出问题
在这里插入图片描述


1-3-2:加 synchronized 锁测试:

在 Java 中,synchronized 使用的是对象级别的锁,也称为内置锁或监视器锁(Monitor Lock)
synchronized 并不是一种特定类型的同步锁,而是 Java 语言内置的一种同步机制,它使用对象级别的锁来确保多线程环境下的数据安全访问。

使用 synchronized 对方法或代码块进行同步;
当一个线程进入 synchronized 方法或代码块时,它会尝试获取对象的锁。如果该锁没有被其他线程占用,该线程就可以进入方法或代码块并执行其中的代码。如果锁已经被其他线程占用,那么该线程就会被阻塞,直到获取到锁为止

创建2个线程同时访问同一个方法,演示多线程下访问同一数据。

在这里插入图片描述


在这里插入图片描述


2、Synchronized 的锁升级过程


锁为什么要升级?

主要为了应对并发的情况,根据并发量去动态的调节锁的实现(状态)。

比如:比如只有一个线程去获取某个锁的时候,此时这把锁就没有必要太消耗系统的资源,所以肯定就采用最低的系统消耗的一种实现方式。如果随着并发量越来越多,当有很多线程去同时竞争这把锁的时候,那么就得考虑性能的问题,去换一种实现方式,根据并发量去动态的调节锁的实现(状态)。


锁的四种实现(状态)

无锁、偏向锁、轻量级锁、重量级锁


1:无锁

无锁,就是没有锁的实现。

比如:我 new 一个【user】 对象,然后没有使用到 synchronized ,就是无锁。

无锁,就是没有对资源进行锁定,所有线程都能访问到统一资源,这就可能出现两种情况:


1-1:无竞争的情况

某个对象不会出现在多线程环境下,或者即使出现在多线程环境下,也不会出现竞争的情况。那么就无需对这个对象进行任何的保护,直接让这个对象给各个线程调用就可以了。


2-1:存在竞争情况,非锁方式同步线程

资源会被竞争,但是我不想对资源进行锁定,不过还是想通过一些机制来控制多线程。
比如说:


CAS机制:
有多个线程要修改同一个值,我们不通过锁定资源的方式,而是通过其他方式来限制,同时只有一个线程能修改成功,而其他修改失败的线程则不断重试,直到修改成功.

CAS 通过操作系统中的一条指令来实现,所以它就能够保证原子性,通过诸如 CAS 这种方式,我们可以进行无锁编程。

大部分情况下,无锁的效率还是挺高的
原因:
1、减少线程阻塞
2、降低线程切换开销
3、避免锁竞争
4、提高可伸缩性
在这里插入图片描述


2:偏向锁

在锁对象的对象头中记录一下当前获取到该锁的线程ID,该线程下次如果又来获取该锁就可以直接获取到了,也就是支持锁重入。

比如:我 new 一个【user】 对象,然后用 synchronized 关键字来锁住这个对象,此时如果只有一个线程来访问这把锁,此时这把锁就是一把偏向锁。


偏向锁主要用来支持可重入锁的实现:

比如:偏向锁会在【user】这个对象的对象头里面记录当前这个线程的 ID ,该线程如果下次又来获取这把锁的这个【user】对象,然后判断这个对象的对象头里面的 ID 是不是当前这个访问线程的 ID,如果是的话,就能重复的获取这把锁,也就能获取到这个【user】对象。


对象头中的 Mark word :锁状态有32位来表示,如果倒数第3bit是1,则这个锁是偏向锁
在这里插入图片描述


3:轻量级锁

当两个或以上的线程 交替 获取锁,但并没有在对象上并发的获取锁时,偏向锁升级为轻量级锁

轻量级锁的底层实现,是通过 CAS 的自旋方式尝试获取锁,避免阻塞线程造成的 cpu 在用户态和内核态间转换的消耗。


比如:有两个或以上的线程,交替着来获取【user】对象的这把锁,那么刚刚的偏向锁就会升级成轻量级锁。



在操作系统中,用户态和内核态是两种不同的运行级别,用来区分程序在执行时所处的特权级别和访问权限。

用户态: 是程序执行的一种状态,只能访问受限的资源,不能访问底层硬设备等特权指令。
大多数应用程序在用户态下运行,包括常见的应用软件和用户自己编写的程序

内核态: 内核态是操作系统的特权级别,具有对系统资源和硬件的完全访问权限,可以执行所有的指令并直接操作系统内核以及硬件设备。

当程序需要执行特权操作(如访问硬件设备、修改系统参数)时,需要从用户态切换到内核态,这个切换过程称为上下文切换

上下文切换涉及到保存和恢复进程的上下文信息,开销相对较大,因此尽量避免频繁的用户态和内核态之间的切换,可以提高系统性能。

轻量级锁使用CAS自旋方式尝试获取锁,避免了线程阻塞从而减少了用户态和内核态之间的频繁切换,提高了性能。


4:重量级锁

两个或以上线程 并发 的在同一个对象上进行同步获取时,为了避免无用自旋消耗 cpu,轻量级锁会升级成重量级锁。


比如:有两个或以上的线程,同时并发的来获取【user】对象的这把锁,那么刚刚的轻量级锁就会升级成重量级锁
就不会去使用 CAS 的自旋锁实现了。
而是通过底层的操作系统来实现这把重量级的锁。
这个过程,就会从用户态,切换到操作系统的内核态。

重量级锁的底层,是通过操作系统提供的互斥锁机制来实现的。
当轻量级锁升级为重量级锁时,会涉及到操作系统层面的资源调度和管理。操作系统会介入并负责锁的管理和调度。
线程在竞争重量级锁时,会进入阻塞状态,操作系统会将这些线程放入一个等待队列中,并在锁可用时唤醒其中的一个线程,让其获取锁并执行临界区的代码(就是被锁修饰的对象或方法的代码)


比如有100个线程去获取这个锁,然后有 99 个线程在自旋的竞争这把锁,那么就很明显的浪费这个cpu的资源,此时升级到重量级锁,到内核态去实现这种互斥,是比较优的方案。


3、如何查看锁的状态

锁,它锁的是对象。

无论是通过 synchronized 代码块修饰对象,还是通过 synchronized 写在方法上,都算是锁对象。synchronized 写在方法上,那么锁的就是 this 对象。

对象的对象头里面存着锁的状态(偏向锁还是轻量锁等),然后就会根据当前这个对象的锁的状态信息来选择不同的实现。
这个状态和实现是同个意思。

4、锁修饰对象和方法的代码


修饰对象:

当synchronized修饰一个对象时,它锁定的是这个对象实例,即当前实例的所有 synchronized 代码块都会受到该锁的影响。

例如:

在这里插入图片描述


修饰方法:

当 synchronized 修饰一个方法时,它锁定的是对象实例,即对于该类的所有实例都是同一把锁。

例如:

在这里插入图片描述


无论是修饰对象还是方法,synchronized都可以确保在任意时刻,最多只有一个线程可以进入被synchronized修饰的代码块或方法,从而避免多个线程同时修改共享资源导致的数据竞争问题。


5、Synchronized 锁的升级总结

Synchronized 锁升级有四种状态, 无锁 – > 偏向锁 – > 轻量级锁 – > 重量级锁 。

1、比如创建一个 user 对象,没有用 synchronized 关键字来修饰,那么这个对象就是 无锁 的状态。

2、接着用 synchronized 修饰这个对象,如果此时有且只有一个线程多次来获取这个锁,要调用这个对象,那么无锁就会升级为 偏向锁

3、接着有两个或两个以上的线程来获取这个锁,但是线程之间是交替来获取这个锁的,交替获取就是指线程A获取到这个锁,操作完再释放后,线程B才会去获取这个锁。这个时候锁就会从偏向锁升级为 轻量级锁
轻量级锁的底层实现是通过 CAS 操作来自旋获取锁。

4、接着两个或两个以上的线程,它们是并发同步来获取这个锁的,那么此时的轻量级锁的CAS操作就顶不住了,就会升级为 重量级锁

重量级锁的底层,是通过操作系统提供的互斥锁机制来实现的。

当轻量级锁升级为重量级锁时,会涉及到操作系统层面的资源调度和管理。操作系统会介入并负责锁的管理和调度。
线程在竞争重量级锁时,会进入阻塞状态,操作系统会将这些线程放入一个等待队列中,并在锁可用时唤醒其中的一个线程,让其获取锁并执行临界区的代码(就是被锁修饰的对象或方法的代码)


这篇关于Synchronized 的锁升级过程介绍(无锁 --> 偏向锁 --> 轻量级锁 --> 重量级锁 )的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/820370

相关文章

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

性能测试介绍

性能测试是一种测试方法,旨在评估系统、应用程序或组件在现实场景中的性能表现和可靠性。它通常用于衡量系统在不同负载条件下的响应时间、吞吐量、资源利用率、稳定性和可扩展性等关键指标。 为什么要进行性能测试 通过性能测试,可以确定系统是否能够满足预期的性能要求,找出性能瓶颈和潜在的问题,并进行优化和调整。 发现性能瓶颈:性能测试可以帮助发现系统的性能瓶颈,即系统在高负载或高并发情况下可能出现的问题

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

作业提交过程之HDFSMapReduce

作业提交全过程详解 (1)作业提交 第1步:Client调用job.waitForCompletion方法,向整个集群提交MapReduce作业。 第2步:Client向RM申请一个作业id。 第3步:RM给Client返回该job资源的提交路径和作业id。 第4步:Client提交jar包、切片信息和配置文件到指定的资源提交路径。 第5步:Client提交完资源后,向RM申请运行MrAp

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

图神经网络模型介绍(1)

我们将图神经网络分为基于谱域的模型和基于空域的模型,并按照发展顺序详解每个类别中的重要模型。 1.1基于谱域的图神经网络         谱域上的图卷积在图学习迈向深度学习的发展历程中起到了关键的作用。本节主要介绍三个具有代表性的谱域图神经网络:谱图卷积网络、切比雪夫网络和图卷积网络。 (1)谱图卷积网络 卷积定理:函数卷积的傅里叶变换是函数傅里叶变换的乘积,即F{f*g}

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现

macOS升级后SVN升级

问题 svn: error: The subversion command line tools are no longer provided by Xcode. 解决 sudo chown -R $(whoami) /usr/local/Cellar brew install svn

Mysql BLOB类型介绍

BLOB类型的字段用于存储二进制数据 在MySQL中,BLOB类型,包括:TinyBlob、Blob、MediumBlob、LongBlob,这几个类型之间的唯一区别是在存储的大小不同。 TinyBlob 最大 255 Blob 最大 65K MediumBlob 最大 16M LongBlob 最大 4G