解密Java多线程中的锁机制:CAS与Synchronized的工作原理及优化策略
目录
- CAS
- 什么是CAS
- CAS的应用
- ABA问题
- 异常举例
- Synchronized 原理
- 基本特征
- 加锁过程
- 偏向锁
- 轻量级锁
- 重量级锁
- 其他优化操作
- 锁消除
- 锁粗化
CAS
什么是CAS
CAS: 全称Compare and swap,字面意思:”比较并交换“,CAS涉及如下操作:
假设内存中的原数据为A,旧的预期值为B ,需要修改的值为C。
- 首先把A与B进行比较,看A与B是否相同。
- 如果A与B相同,则把数据C的值赋予A。
- 返回操作成功。
我们来写一个CAS的伪代码以帮忙我们更好理解CAS。
boolean Cas(int a,int b,int c){//进行比较看a是否发生变化if(a==b){a=c;return true;}return false;}
CAS是乐观锁的一种实现方式,当多个线程对一个数据进行操作时,只有一个线程操作成功,其他线程并不会阻塞,会返回操作失败的信号。
真实的 CAS 是一个原子的硬件指令完成的,只有硬件予以支持,软件方面才能实现。
CAS的应用
标准库中提供了 java.util.concurrent.atomic 包,里面的类都是基于这种方式来实现的。
典型的就是 AtomicInteger 类, 其中的 getAndIncrement 相当于 i++ 操作。
public static void main(String[] args) {ReentrantReadWriteLock lock = new ReentrantReadWriteLock();AtomicInteger seq = new AtomicInteger(0);//进行++操作seq.getAndIncrement();seq.getAndIncrement();seq.getAndIncrement();System.out.println(seq);}

我们点开自增方法,我们看到它的操作也是通过上述伪代码的那种方式实现的。

也可以使用CAS实现自旋锁
ABA问题
假设存在两个线程 t1 和 t2。 有一个共享变量 num, 初始值为 A。
接下来,线程 t1 想使用 CAS 把 num 值改成 Z,那么就需要
- 先读取 num 的值, 记录到 oldNum 变量中。
- 使用 CAS 判定当前 num 的值是否为 A, 如果为 A,就修改成 Z。
但是,在 t1 执行这两个操作之间,t2 线程可能把 num 的值从 A 改成了 B, 又从 B 改成了 A。
异常举例
以银行取钱为例:
- 存款 100,线程1 获取到当前存款值为 100,期望更新为 50; 线程2 获取到当前存款值为 100, 期望更新为 50。
- 线程1 执行扣款成功, 存款被改成 50。线程2 阻塞等待中。
- 在线程2 执行之前, 你的朋友正好给你转账 50, 账户余额变成,100。
- 轮到线程2 执行了,发现当前存款为 100,和之前读到的 100 相同,再次执行扣款操作。
这样我们的钱就不翼而飞了,所以这种情况是万万不可的。
所以我们引入版本号来解决这个问题。CAS在读取旧值时也要读取版本号,在修改时,如果读到的版本号与当前版本号相同就进行修改,如果当前版本号高于读到的版本号,就修改失败。
Synchronized 原理
基本特征
- 开始时是乐观锁,如果锁冲突严重就升级为悲观锁。
- Synchronized是可重入锁。
- 是不公平锁。
- 是不可读写锁
- 开始是轻量级锁实现,如果锁被持有的时间较长, 就转换成重量级锁。
加锁过程
加锁流程图:

偏向锁
偏向锁就是在当前锁对象中标记改锁属于那个线程,没有进行实际加锁,能不加锁就不加锁,减少不必要的开销,只有当其他线程来竞争锁时,才会进行锁升级,由偏向锁变为轻量级锁。
轻量级锁
锁升级为轻量级锁之后,通过CAS实现。
- 通过CAS检查并更新一块内存。
- 如果更新成功,则认为加锁成功。
- 如果更新失败,则认为加锁失败,锁被占用
重量级锁
如果竞争进一步激烈, 自旋不能快速获取到锁状态,就会膨胀为重量级锁
此处的重量级锁就是指用到内核提供的 mutex。
- 执行加锁操作, 先进入内核态。
- 在内核态判定当前锁是否已经被占用
- 如果该锁没有占用, 则加锁成功,并切换回用户态。
- 如果该锁被占用,则加锁失败。 此时线程进入锁的等待队列,挂起。 等待被操作系统唤醒。
- 经历了一系列的沧海桑田, 这个锁被其他线程释放了, 操作系统也想起了这个挂起的线程, 于是唤醒这个线程, 尝试重新获取锁。
当多个线程竞争同一把锁,自旋等待的时间过长,无法获取到锁时,JVM会将这把锁升级为重量级锁。这时,线程并不再进行自旋等待,而是进入内核态,通过操作系统提供的mutex实现来管理锁的状态和等待队列。
在内核态中,操作系统判定当前锁是否已经被占用。如果锁没有被占用,则线程成功获取到锁,并切换回用户态继续执行。如果锁已经被占用,则线程加锁失败。此时,线程会进入锁的等待队列,并被操作系统挂起,等待被唤醒。
随着时间的推移和线程的竞争,当其他线程释放了这把锁并且操作系统意识到有线程在等待这个锁时,操作系统会唤醒等待的线程,使其重新启动并尝试重新获取锁。这个过程可能会经历一段时间,之后线程再次尝试获取锁以继续执行。
其他优化操作
锁消除
编译器+JVM 判断锁是否可消除,如果可以,就直接进行消除了。
也就是说我们许多加锁操作在单线程中运行时,那些加锁操作的锁就没必要。
@Overridepublic synchronized StringBuffer append(String str) {toStringCache = null;super.append(str);return this;}
例如 StringBuffe中的append操作就会涉及加锁操作,我们在单线程运行中就可以进行锁消除。
锁粗化
一段逻辑中如果出现多次加锁解锁,编译器 + JVM 会自动进行锁的粗化。
用我们上课讲的例子就是:
领导给下面人布置任务呢,一共三个任务,现在有这两种做法:
- 给员工打一个电话一次性什么三个任务。
- 给员工打三个电话,一次说一个任务。
让我们大家选择,大家肯定选择做法一啊,当然人家jvm也会进行这样的锁粗化。
可以用一个代码理解一下:
//频繁加锁for (int i = 0; i < 100; i++) {synchronized (o1){}}//粗化synchronized (o1){for (int i = 0; i < 100; i++) {}}
把锁粗化,避免频繁申请释放锁。
相关文章:
解密Java多线程中的锁机制:CAS与Synchronized的工作原理及优化策略
目录 CAS什么是CASCAS的应用ABA问题异常举例 Synchronized 原理基本特征加锁过程偏向锁轻量级锁重量级锁 其他优化操作锁消除锁粗化 CAS 什么是CAS CAS: 全称Compare and swap,字面意思:”比较并交换“,CAS涉及如下操作: 假设内存中的原数据…...
solid works草图绘制与设置零件特征的使用说明
(1)草图绘制 • 草图块 在 FeatureManager 设计树中,您可以隐藏和显示草图的单个块。您还可以查看块是欠定义 (-)、过定义 () 还是完全定义。 要隐藏和显示草图的单个块,请在 FeatureManager 设计树中右键单击草图块,…...
vue3使用router.push()页面跳转后,该页面不刷新问题
文章目录 原因分析最优解决 原因分析 这是一个常见问题,当使用push的时候,会向history栈添加一个新记录,这个时候,再添加一个完全相同的路由时,就不会再次刷新了 最优解决 在页面跳转时加上params参数时间 router.…...
如何理解数字工厂管理系统的本质
随着科技的飞速发展和数字化转型的推动,数字工厂管理系统逐渐成为工业4.0时代的重要工具。数字工厂系统旨在整合和优化工厂运营的各个环节,通过实时数据分析和处理,提升生产效率,降低成本,并增强企业的整体竞争力。为了…...
笔记1.3 数据交换
如何实现数据通过网络核心从源主机到达目的主机? 数据交换 交换网络: 动态转接动态分配传输资源 数据交换类型: (1)电路交换 (2)报文交换 (3)分组交换 电路交换的特…...
实时车辆行人多目标检测与跟踪系统(含UI界面,Python代码)
算法架构: 目标检测:yolov5 目标跟踪:OCSort其中, Yolov5 带有详细的训练步骤,可以根据训练文档,训练自己的数据集,及其方便。 另外后续 目标检测会添加 yolov7 、yolox,目标跟踪会…...
谷歌AI机器人Bard发布强大更新,支持插件功能并增强事实核查;全面整理高质量的人工智能、机器学习、大数据等技术资料
🦉 AI新闻 🚀 谷歌AI机器人Bard发布强大更新,支持插件功能并增强事实核查 摘要:谷歌的人工智能聊天机器人Bard发布了一项重大更新,增加了对谷歌应用的插件支持,包括 Gmail、Docs、Drive 等,并…...
NI SCXI-1125 数字量控制模块
NI SCXI-1125 是 NI(National Instruments)生产的数字量控制模块,通常用于工业自动化和控制系统中,以进行数字输入和输出控制。以下是该模块的一些主要产品特点: 数字量输入:SCXI-1125 模块通常具有多个数字…...
链表oj题1(Leetcode)——移除链表元素,反转链表,链表的中间节点,
链表OJ 一,移除链表元素1.1分析1.2代码 二,找到链表的中间节点2.1分析2.2代码 三,反转链表3.1分析3.2代码 四,找到链表中倒数第k个节点4.1分析4.2代码 一,移除链表元素 移除链表元素 1.1分析 这里的删除要分成两种…...
【libuv】与uvgrtrp的_SSIZE_T_定义不同
libuv的 #if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED) typedef intptr_t ssize_t;...
安卓ROM定制 修改必备常识-----初步了解system系统分区文件夹的基本含义 【二】
安卓修改rom 固件 修改GSI 移植rom 必备常识 lib--**so文件基本解析 一起来了解system目录相应文件的用途吧。(rom版本不同里面的app也会不一样) 简单打开img格式后缀文件 给大家说下最简单的方法提取img里面的文件,对于后缀img格式的文件可…...
GPT会统治人类吗
一 前言 花了大概两天时间看完《这就是ChatGPT》,触动还是挺大的,让我静下来,认真地想一想,是否真正理解了ChatGPT,又能给我们以什么样的启发。 二 思考 在工作和生活中,使用ChatGPT或文心一言,…...
win系统环境搭建(六)——Windows安装nginx
windows环境搭建专栏🔗点击跳转 win系统环境搭建(六)——Windows安装nginx 本系列windows环境搭建开始讲解如何给win系统搭建环境,本人所用系统是腾讯云服务器的Windows Server 2022,你可以理解成就是你用的windows10…...
Java中使用BigDecimal类相除保留两位小数
问题 遇到2个数相除,需要保留2位小数的结果。 解决 BigDecimal sum ...; BigDecimal yearValue ...;MathContext mathContext new MathContext(2, RoundingMode.DOWN); yearValue.divide(sum, mathContext);...
激光雷达在ADAS测试中的应用与方案
在科技高速发展的今天,汽车智能化已是必然的趋势,且自动驾驶汽车的研究也在世界范围内进行得如火如荼。而在ADAS测试与开发中,激光雷达以其高性能和高精度占据着非常重要的地位,它是ADAS测试与开发中不可缺少的组成。 一 激光雷达…...
malloc与free
目录 前提须知: malloc: 大意: 头文件: 申请空间: 判断是否申请成功: 使用空间: 结果: 整体代码: malloc申请的空间怎么回收呢? 注意事项: free:…...
计算周包材,日包材用来发送给外围系统
文章目录 1 Introduction2 code 1 Introduction In this example We get data from BOM and RESB . and calculate it . 2 code TYPES: BEGIN OF TY_ZPPT_0015_W,AUFNR TYPE ZPPT_0015-AUFNR,ZXH TYPE ZPPT_0015-ZXH,ZZJHID TYPE ZPPT_0015-ZZJHID,ZRJHID TYPE Z…...
R语言柱状图直方图 histogram
柱状图简介 柱状图也叫直方图,是展示连续性数值的分布状况。在x轴上将连续型数值分为一定数量的组,y轴显示对应值的频数。 R基本的柱状图 hist 我们用R自带的Orange数据来画图。 > head(Orange)Tree age circumference(圆周长) 1 1 118 …...
Linux磁盘管理:最佳实践
🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🦄 博客首页——🐅🐾猫头虎的博客🎐 🐳 《面试题大全专栏》 🦕 文章图文…...
uni-app:通过三目运算动态增加样式效果(class)
效果 代码 第一条:当变量line的值等于abc时,class就等于yes,反之class等于no(显然等于abc,执行yes,前景色为红色) 第一条:当变量line1的值等于abc时,class就等于yes,反之class等于noÿ…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...
golang循环变量捕获问题
在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - 循环变量捕获问题。让我详细解释一下: 问题背景 看这个代码片段: fo…...
cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
Python Ovito统计金刚石结构数量
大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...
mac 安装homebrew (nvm 及git)
mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用: 方法一:使用 Homebrew 安装 Git(推荐) 步骤如下:打开终端(Terminal.app) 1.安装 Homebrew…...
MySQL 8.0 事务全面讲解
以下是一个结合两次回答的 MySQL 8.0 事务全面讲解,涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容,并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念(ACID) 事务是…...
