CAS以及synchronized

news2024/9/9 5:35:19

什么是CAS?

CAS全称“Compare and swap”,字面意思就是“比较并交换”

CAS有什么用?

实现了无锁编程,这就意味着减少了线程之间的阻塞和竞争。并且CAS操作是原子性的,保证了数据的一致性

工作原理

假设内存中的原数据为V,旧的预期值为A,新的需要修改的值为B

1.首先比较 V 和 A 的值是否相等

2.如果比较相等则将 V 的值更改为 B 的值

3.返回结果,如果更新成功返回 true,如果失败返回 false( V 的值和 A 不相等,即 V 已经被其他线程修改了)

针对第3点来说,既然CAS的操作是原子性的,那么为什么 V 会被其他的线程修改呢?

这是因为 CAS 操作只保证了比较和交换是原子性的,并不能保证在这之前和之后的操作是原子性的,可能在进行比较之前 V 的值已经被其他线程进行了修改,此时再和 A 进行比较那么因为值不相同所以就会返回 false

ABA问题

假定现在有一个共享数据,其初始值为A,线程1可能先读取到这个值A,但是在进行修改的时候,线程2插入进来了读取到了A的值并且修改成了B,然后又因为某种原因修改成了A,最终对于线程1来说这个数据是没有发生改变的

来举一个具体的场景吧

人物:滑稽1,滑稽2

场景:滑稽1 给 滑稽2 转账500元

初始值:滑稽1 账户1000元,滑稽2 账户1000元

正常情况:

1.线程1 执行了转账 500 元之后, 滑稽1 的账户余额就从 1000 变成了 500 ,线程2进入阻塞等待

2.轮到线程2 执行的时候发现滑稽1 的账户余额(500)与初始余额(1000)对不上号之后,就不再执行这个操作了 

异常情况:

1.线程1 执行完转账操作之后,账户余额从1000变成了500,此时线程2进入阻塞等待中

2.在线程2执行之前,朋友C突然想起来还欠滑稽1   500元钱,于是给滑稽1 转账了500元,这时滑稽1的账户余额又从500变成了1000

3.当线程2开始执行的时候,比较账户余额和初始余额的值是相同的,发现相同,于是又进行了转账500的操作,所以滑稽1的账户余额又从1000变成了500,在这个过程中滑稽1 进行了两次转账操作,多转了500

那么针对上述这个异常情况的场景我们应该怎样解决呢?

版本号

针对上面这个场景我们可以引入版本号来解决?版本号有什么用?

  • CAS在读取旧值的同时也要读取版本号
  • 修改的时候:
    • 1.如果当前版本号与读取到的版本号相同,那么修改数据,并把版本号+1
    • 2.如果当前版本号大于读取到的版本号,就认为操作失败,因为数据已经被修改过了

情景演绎:

还是针对上述的场景:

1.我们将账户余额的版本号设为 1 

2.线程1 执行转账操作之后,账户余额从1000变成了500,版本号从1变成2,线程2进入阻塞等待

3.朋友C给滑稽1 转账500 ,此时账户余额从500变成了1000,版本号从2变成了3

4.线程2开始执行转账操作,发现虽然账户余额(1000)与初始账户(1000)一样,但是当前版本号(3)大于读到的版本号(1),于是这个转账操作就不会进入

什么是synchronized

在上一篇博客中我们介绍到了什么是锁策略,那么现在来讲讲synchronized的基本原理

  1. 开始是乐观锁,如果锁冲突频繁则转化成悲观锁
  2. 开始是轻量级锁,如果锁被持有的时间比较长,那么就会转换成重量级锁
  3. 实现轻量级锁大概率用到的是自旋锁策略
  4. 是一种不公平锁
  5. 是一种可重入锁
  6. 不是读写锁

加锁过程

JVM会将synchronized分为无锁,偏向锁,轻量级锁,重量级锁,并根据情况,依次进行升级

偏向锁

第一次尝试加锁的过程中,会先进入偏向锁转态,偏向锁并不是真的给线程进行加锁,而是相当于给线程做个标记,标记这个锁是属于哪个线程的,如果后续没有其他线程来竞争这个锁,那么就不用进行同步操作了(避免了加锁解锁的开销),如果后续有其他线程来竞争该锁,也能很容易识别当前申请锁的线程是不是之前进行标记的线程,如果不是那就取消偏向锁的状态进入轻量级锁,说白了偏向锁的本质就是“延迟加锁”,并不是真正意义上的加锁

轻量级锁

偏向锁升级成轻量级锁的过程发生在多个线程同时竞争一把锁的情况下,当一个线程已经获得了偏向锁,另一个线程试图获取该锁时,原先的偏向锁转态将被撤销,并转换成轻量级锁

升级过程:

  1. 如果该线程没有锁则JVM则会给该线程加上一把偏向锁,并将该线程的 Mark Word 设置 成偏向模式,同时记录下该线程的 ID
  2. 如果其他线程尝试获取该锁,JVM则会检查 Mark Word 中的线程 ID 是否与当前线程的 ID相同,如果不相同说明存在锁竞争问题
  3. JVM会先撤销原来的偏向锁,并将 Mark Word 从偏向模式变成无锁模式,这会让之前被记录的 ID 被清除
  4. 撤销偏向锁后会尝试,新的线程会尝试获取轻量级锁。轻量级锁尝试用 CAS 来获取该锁:

            ①新线程会尝试使用CAS将自己线程的 ID 写入 Mark Word中

            ②如果CAS成功,则说明没有其他线程竞争该锁,新线程获取轻量级锁

            ③如果CAS失败,则说明其他线程正在尝试获取该锁,或者该锁已经被其他线程锁拥有

      5.如果多次CAS失败则会从轻量级锁变成重量级锁

重量级锁

如果竞争激烈,自旋不能快速获取到锁状态,则就会变成重量级锁

  • 执行加锁操作,先进入内核态
  • 在内核态判定当前锁是否已经被占用
  • 如果该锁没有被占用,则加锁成功,并切换回用户态
  • 如果该锁被占用,则加锁失败,此时线程进入等待锁队列,挂起,直到被操作系统唤醒

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

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

相关文章

postgresql 您要的日期查询都在这

1、获取当前日期 select now();select current_timestamp;返回值均是当前年月日、时分秒,且秒保留6位小数,两种方式等价 select current_time;返回值:时分秒,秒最高精确到6位 select current_date;返回值:年月日 2…

HarmonyOS 鸿蒙DFX能力简介

DFX简介: Development and Feedback eXchange(‌DFX)‌,‌用于开发、‌测试和维护鸿蒙应用,提供一系列的工具和功能,‌帮助开发者在开发过程中进行性能分析、‌故障检测、‌异常处理。比如异常处理、性能分…

【qt小系统】传感器云平台3D散点图(附源码)

摘要:本文主要使用QT5,实现了一个传感器云平台的小示例,模拟的是各类传感器的添加,例如:热成像传感器、温度传感器、超声波传感器,模拟添加完成后,会自动将此传感器的三维坐标增加到3D散点图上&…

【每日一篇】UrbanGPT:时空大语言模型 【方便自己看】

摘要 时空预测旨在预测和洞察城市环境在时间和空间上不断变化的动态。它的目的是预测未来的模式,趋势和城市生活的各个方面的事件,包括交通,人口流动和犯罪率。虽然已经有许多努力致力于开发神经网络技术来准确预测时空数据,但重…

【C 语言】深入理解冒泡排序算法

0. 前言 冒泡排序是一种经典且基础的排序算法。它虽然在效率上并非最优,但对于初学者理解排序的基本概念和逻辑有着重要的意义。 1. 冒泡排序的基本思想 冒泡排序的基本思想是通过反复比较相邻的元素并交换它们(如果顺序错误),…

基于GEC6818开发板+Linux+Qt设计的智能养老院出入管理系统(195)

一、前言 1.1 项目介绍 【1】项目功能介绍 随着我国老龄化进程的加快,养老问题日益突出,如何有效保障老年人的生活质量与安全成为社会关注的重点。智能化、信息化技术的发展为解决这一问题提供了新的思路和手段。基于Linux系统的智能养老院出入管理系统应运而生,为了实现…

记录一次使用Docker部署skywalking的过程

临时一个测试系统需要追一下bug,所以计划单节点部署一套skywalking进行调用链分析。 网上扒拉了几篇,都有点问题,这里单独记录一个。 首先skywalking需要是用es做数据源,当然也有mysql等多个版本,这里用的es。 同时…

使用JavaFx Fxml笔记

使用JavaFx Fxml实现账号密码登录 HelloApplication.java:package com.example.dr295cmonth7;import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.geometry.Insets; import javafx.scene.Parent; import javafx.scene.Scene; i…

Pinely Round 4 (Div. 1 + Div. 2)

有不明白或者想交流一下的可以加一下扣扣:2674993642,博客一般写的时候才看 A. Maximize the Last Element 解析:题目要让一次删除两个相邻的数字,最后留下尽可能最大的数字,赛时我用笔模拟了一下,发现如…

c++11,左值引用和右值引用,右值引用的作用

目录 左右值引用概念 右值引用的作用 左右值引用概念 什么是左值?什么是左值引用? 左值是一个表示数据的表达式(如变量名或解引用的指针),我们可以获取它的地址可以对它赋 值,左值可以出现赋值符号的左边,右值不能出…

最详细最新最简单的Jenkins安装使用

首先去jenkins官网下载最新LTS war版本https://www.jenkins.io/download/ 准备sdk包 、nginx配置文件 、已配置的maven文件 查看系统架构 cat /etc/redhat-release uname -a uname -m lscpu安装常用工具 yum install jq -y yum install git -yyum install nginx -y systemc…

深入源码:解析SpotBugs (6)jvm 字节码简介

文章目录 一、JVM字节码概述一、文件结构概述二、详细解析1. 魔数和Class文件的版本2. 常量池3. 访问标志4. 类索引、父类索引与接口索引集合5. 字段表和方法表6. 属性表 字节码Spotbugs 作为一名资深的Java开发工程师,对JVM及其字节码有着深入的理解。现在&#xf…

DRAM 和 NAND 闪存收入将在 2024 年显著增长 75% 和 77%

#### 市场概况 根据 TrendForce 最新发布的市场报告,预计 2024 年 DRAM 和 NAND 闪存的收入将分别显著增长 75% 和 77%,这一增长主要是由于平均价格的上涨以及高价值产品的兴起,例如 HBM(高带宽内存)和 QLC&#xff0…

卷积神经网络(六)---实现 cifar10 分类

cifar10 数据集有60000张图片,每张图片的大小都是 32x32 的三通道的彩色图,一共是10种类别、每种类别有6000张图片,如图4.27所示。 图 4.27 cifar数据集 使用前面讲过的残差结构来处理 cifar10 数据集,可以实现比较高的准确率。 …

配置本地开发服务器代理请求以及登录模块开发(二)

项目初始化完成之后,准备开始进行项目的开发,首先配置好开发环境作为整个项目的基础 一、配置代理 1、config/proxy.ts配置代理 export default {// 如果需要自定义本地开发服务器 请取消注释按需调整dev: {// localhost:8000/api/** -> https://p…

Seata 入门与实战

一、什么是 Seata Seata 是一款开源的分布式事务解决方式,致力于提供高性能和简单易用的分布式事务服务。Seata 为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式事务解决方案。 二、Seata 组成 事务协调者(Transacti…

什么是Shell?怎么编写和执行Shell脚本?

大家好呀!今天来简单介绍一下Shell基础,Shell介于内核与用户之间,是一个命令解释器,负责命令的解释。简单理解,Shell既是一个程序也是一种脚本语言。 1、shell介绍 1.1 概述 shell介于内核与用户之间,是一个…

索引结构—B+Tree索引、Hash索引、Full-Text(全文)索引、R-Tree(空间)索引

一、概述 在数据库系统中,索引是一种用于加快数据检索的数据结构。不同的索引结构适用于不同的查询场景和数据特性。索引按照不同角度可以划分不同类型的索引。按照数据结构可以划分BTree索引、Hash索引、FULL TEXT(全文)索引、R-Tree&#…

python inf是什么意思

INF / inf:这个值表示“无穷大 (infinity 的缩写)”,即超出了计算机可以表示的浮点数的范围(或者说超过了 double 类型的值)。例如,当用 0 除一个整数时便会得到一个1.#INF / inf值;相应的,如果…

卡码网KamaCoder 103. 水流问题

题目来源&#xff1a;103. 水流问题 C题解&#xff1a;从边界往高处走&#xff0c;走过的地方做标记。第一组边界跟第二组边界能走到的地方取交集。 代码来源代码随想录。&#xff08;虽然思路一样&#xff0c;但人家代码写得比我好哇&#xff09; #include <iostream>…