java多线程操作之CAS

news/2024/9/13 23:56:23/ 标签: java, jvm, 开发语言

1,什么是CAS?

CAS(Compare-And-Swap) 比较并交换,用于实现同步和锁机制。经常配合juc中Atomic相关类进行。Atomic相关类无法解决aba问题。


2,CAS核心思想是什么?

比较和交换。
本质上就是乐观锁 + 自旋
这里需要注意的是CAS能保证原子性是介于使用CAS时,比较对象采用了Atomic相关类。如果没有,则不保证原子性。


3,CAS的基本概念

三个操作数
对象: 要比较和可能修改的对象
预期值:当前对象的预期
新值:如果与预期值相等,则更新为新值
两个步骤
比较:比较当前对象是否与预期相等
交换:如果当前对象与预期相等,则交换更新为新值


4,CAS锁的实现

CAS 锁是一种基于 CAS 操作实现的轻量级锁,通常用于实现自旋锁。它的主要优点是避免了传统锁机制的上下文切换开销,提高了并发性能。


4.1 基本实现

java">package com.rojer.threadCAS;import java.util.concurrent.atomic.AtomicInteger;/*** 一般情况都配合Atomic类保证其原子性*/
public class CASExample {private AtomicInteger value = new AtomicInteger(0);/*** compareAndSet(CAS)方法的底层原理基于硬件支持的原子操作,* 通过 Unsafe 类封装实现。在现代处理器中,CAS 操作通常通过硬件指令(如 x86 架构上的 cmpxchg 指令)实现。* 这些硬件指令能够在不被其他线程打断的情况下完成读取、比较和写入操作,从而实现原子性。*/public void increment() {int oldValue, newValue;do {oldValue = value.get();newValue = oldValue + 1;} while (!value.compareAndSet(oldValue, newValue));}public int getValue() {return value.get();}/*** 错误的示例*/private Integer value1 = 0;public void increment1() {// value1++无法保证原子性操作value1++;}public int getValue1() {return value1;}public static void main(String[] args) {CASExample example = new CASExample();for (int i = 0; i < 1000; i++) {new Thread(example::increment).start();}// 由于多线程的原因,可能需要等待所有线程执行完// 这里简单等待try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("AtomicInteger 验证CAS,值为:" + example.getValue());for (int i = 0; i < 1000; i++) {new Thread(example::increment1).start();}// 由于多线程的原因,可能需要等待所有线程执行完// 这里简单等待try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Integer 验证CAS,值为:" + example.getValue1());}
}

执行测试

4.2 当是自定义object为比较对象时

java">package com.rojer.threadCAS;import java.util.concurrent.atomic.AtomicStampedReference;/*** 测试CAS比较为一个对象时,避免ABA问题*/
public class CustomObjectCASExample {// 创建测试静态内部类private static class CustomObject {private final int id;public CustomObject(int id) {this.id = id;}public int getId() {return id;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;CustomObject that = (CustomObject) o;return id == that.id;}@Overridepublic int hashCode() {return Integer.hashCode(id);}}// 对象保证原子性使用AtomicStampedReference, 这里第二个参数就是类似版本号,防止ABA问题产生private final AtomicStampedReference<CustomObject> reference =new AtomicStampedReference<>(new CustomObject(0), 0);public void update(int newId) {CustomObject oldObject, newObject;int oldStamp, newStamp;do {oldObject = reference.getReference();oldStamp = reference.getStamp();newObject = new CustomObject(newId);newStamp = oldStamp + 1;} while (!reference.compareAndSet(oldObject, newObject, oldStamp, newStamp));}public CustomObject getObject() {return reference.getReference();}public static void main(String[] args) {CustomObjectCASExample example = new CustomObjectCASExample();for (int i = 0; i < 1000; i++) {final int id = i;new Thread(() -> example.update(id)).start();}// 由于多线程的原因,可能需要等待所有线程执行完// 这里简单等待try { Thread.sleep(1000); } catch (InterruptedException e) { }System.out.println(example.getObject().getId());}}

4.3可重入锁的情况 -- 这里已经不是CAS的逻辑了。做额外展示(没有自旋和比较情况下,如何保证原子性)

java">package com.rojer.threadCAS;import java.util.concurrent.locks.ReentrantLock;/*** CAS之使用可重入锁* 使用可重入锁之后,就不需要自选比较,因为可重入锁也是独占锁*/
public class ReentrantLockExample {private int value = 0;private final ReentrantLock lock = new ReentrantLock();public void increment() {lock.lock();try {value++;} finally {lock.unlock();}}public int getValue() {return value;}public static void main(String[] args) {ReentrantLockExample example = new ReentrantLockExample();for (int i = 0; i < 100; i++) {new Thread(example::increment).start();}// 由于多线程的原因,可能需要等待所有线程执行完// 这里简单等待try { Thread.sleep(1000); } catch (InterruptedException e) { }System.out.println(example.getValue());}
}

5, 优点和缺点

优点:
低延迟: 避免传统锁的上下文切换开销
无锁:减少了线程阻塞和唤醒的开销

缺点:
自旋开销: 在高并发场景自旋开销可能过大
适用性: 不适合持有锁的操作,长时间自旋会影响系统性能。
可能导致ABA问题

6,展开1--什么是ABA问题?

1,线程 A 执行 CAS 操作,检查一个值(假设为 A)并希望将其更新为另一个值(假设为 B)。
2, 在 A 线程执行 CAS 操作期间,另一个线程 B 将值从 A 更新为另一个值 C,然后又将其更新回 A。
3, 当线程 A 再次执行 CAS 操作时,它仍然看到值为 A,并认为数据没有被改变,因此成功地将值更新为 B,但实际上,数据在 A 和 B 之间发生了变化。
ABA 问题 的关键在于 CAS 操作无法区分值是否在检查期间被修改和恢复,导致线程 A 在看似安全的操作下可能会做出错误的决策。

7,展开2--为什么Atomic类能保证原子性

atomic类方法底层都采用了Unsafe 类的方法。

Unsafe 类提供了直接操作内存和硬件级别原子操作的方法。compareAndSwapIntUnsafe 类中的一个本地方法(native),通过 JNI 调用底层硬件指令实现原子操作。

  • obj:需要操作的对象。
  • offset:对象中字段的内存偏移量。
  • expected:期望值。
  • x:更新值。

8,展开3--JUC中使用到CAS的地方以ConcurrentHashMap为例


http://www.ppmy.cn/news/1475501.html

相关文章

计算1的数量

1. 计算1的数量 题目ID&#xff1a;9809必做题100分 最新提交&#xff1a; Accepted 100 分 历史最高&#xff1a; Accepted 100 分 时间限制: 1000ms 空间限制: 524288kB 题目描述 给定一个n*m的二进制矩阵&#xff0c;请你数一数矩阵中完全被0上下左右包围的1的数…

樊登读书精准表达

阅读建议:本书解读过程中,刘蔚涛老师展示了很多精彩图表,建议配合视频,效果更好。 书友你好,欢迎来到非凡精读馆,我是刘蔚涛。 今天给大家带来一本好书,名字叫作《精准表达》,副标题是“怎么让你的方案在最短的时间内打动人心”。这本书2004年出版,出版后在日本畅销…

MySQL 日志深度解析:从查询执行到性能优化

引言 MySQL 日志是数据库管理员和开发者的宝贵资源&#xff0c;它提供了查询执行的详细情况&#xff0c;帮助我们诊断问题和优化性能。本文将深入分析一个具体的 MySQL 日志条目&#xff0c;解释其含义&#xff0c;并提供针对性的优化建议。 日志信息概览 让我们先来快速了解…

Perl编译器架构:前端与后端的精细分工

&#x1f527; Perl编译器架构&#xff1a;前端与后端的精细分工 Perl作为一种高级、通用的编程语言&#xff0c;其编译器的架构设计对于性能和灵活性至关重要。Perl编译器由前端和后端组成&#xff0c;它们各自承担着不同的职责。本文将深入解析Perl编译器前端和后端的区别&a…

Gradio聚类

为了增加页面输出聚类后的结果并方便可视化分析聚类效果&#xff0c;下面是更新后的代码。将Gradio界面中的输出类型改为gr.outputs.HTML&#xff0c;并在返回结果时生成HTML格式的聚类结果。python import gradio as gr from transformers import AutoTokenizer, AutoModel i…

绝区捌--将GPT幻觉的发生率从20%以上降低到2%以下

总结&#xff1a;我们没有使用微调&#xff0c;而是结合使用提示链和预处理/后处理来将幻觉发生率降低一个数量级&#xff0c;但这确实需要对 OpenAI 进行 3-4 倍的调用。还有很大的改进空间&#xff01; 使用 GPT 等大型语言模型面临的最大挑战之一是它们倾向于捏造信息。 这…

关于maven工程编译的一些问题

首先抛问题&#xff0c;在maven clean的时候出现下面的错误&#xff1a; 错误源代码如下&#xff1a; [ERROR] The build could not read 1 project -> [Help 1] [ERROR] [ERROR] The project com.**:**:2.0.0 (D:\JAVA\**-policy\pom.xml) has 1 error [ERROR] N…

音视频开发—FFmpeg处理流数据的基本概念详解

文章目录 多媒体文件的基本概念相关重要的结构体操作数据流的基本步骤1.解复用&#xff08;Demuxing&#xff09;2.获取流&#xff08;Stream&#xff09;3. 读取数据包&#xff08;Packet&#xff09;4. 释放资源&#xff08;Free Resources&#xff09;完整示例 多媒体文件的…

【自动驾驶/机器人面试C++八股精选】专栏介绍

目录 一、自动驾驶和机器人技术发展前景二、C在自动驾驶和机器人领域的地位三、专栏介绍四、订阅需知 一、自动驾驶和机器人技术发展前景 随着人工智能、机器学习、传感器技术和计算能力的进步&#xff0c;自动驾驶和机器人的技术水平不断提升&#xff0c;使得它们更加智能、可…

js项目生产环境中移除 console

1、terser-webpack-plugin webpack 构建的项目中安装使用 安装&#xff1a; npm install terser-webpack-plugin --save-dev 配置 在webpack.config.js文件中 new TerserPlugin({terserOptions: {output: {comments: false, // 去除注释},warnings: false, // 去除黄色警告,co…

Python酷库之旅-第三方库Pandas(021)

目录 一、用法精讲 52、pandas.from_dummies函数 52-1、语法 52-2、参数 52-3、功能 52-4、返回值 52-5、说明 52-6、用法 52-6-1、数据准备 52-6-2、代码示例 52-6-3、结果输出 53、pandas.factorize函数 53-1、语法 53-2、参数 53-3、功能 53-4、返回值 53-…

在InternStudio上创建一台GPU服务器

填写配置 创建完成 ssh连接&#xff0c;并测试常用指令 查看开发机信息 查看gpu信息 创建conda环境 跑个test

康谋分享 | 自动驾驶联合仿真——功能模型接口FMI(三)

在之前的两篇文章中&#xff08;文末往期回顾中可查看&#xff09;&#xff0c;我们主要介绍了功能模型接口FMI的主要组成部分和一些使用场景&#xff0c;今天就以康谋自动驾驶仿真软件aiSim为例&#xff0c;来展示一下如何建立一个FMU并实现基于UDP和FMI联合仿真&#xff08;c…

GitHub+Picgo图片上传

Picgo下载&#xff0c;修改安装路径&#xff0c;其他一路下一步&#xff01; 地址 注册GitHub&#xff0c;注册过程不详细展开&#xff0c;不会的百度一下 地址 新建GitHub仓库存放图片 生成Token令牌 点击头像&#xff0c;点击Settings 滑到最后 过期时间&#xff1a;No expi…

MyBatisPlus实现增删改查

文章目录 MyBatisPlus实现增删改查基本操作分页查询配置分页插件 MyBatisPlus实现增删改查 实体类GkUser package com.geekmice.springbootselfexercise.entity;import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField;…

uniapp 初始学习1

uni-app代码基本包括js,vue,css.在app端支持原生渲染nvue&#xff0c;可编译的kotlin和swift 掌握js就可以进行不同应用的开发 页面文件遵循 Vue 单文件组件 (SFC) 规范&#xff0c;即每个页面是一个.vue文件 .vue文件是一个自定义的文件类型&#xff0c;用类HTML语法描述一…

2024年中国网络安全市场全景图 -百度下载

是自2018年开始&#xff0c;数说安全发布的第七版全景图。 企业数智化转型加速已经促使网络安全成为全社会关注的焦点&#xff0c;在网络安全边界不断扩大&#xff0c;新理念、新产品、新技术不断融合发展的进程中&#xff0c;数说安全始终秉承科学的方法论&#xff0c;以遵循…

02:项目二:感应开关盖垃圾桶

感应开关盖垃圾桶 1、PWM开发SG901.1、怎样通过C51单片机输出PWM波&#xff1f;1.2、通过定时器输出PWM波来控制SG90 2、超声波测距模块的使用3、感应开关盖垃圾桶 需要材料&#xff1a; 1、SG90舵机模块 2、HC-SR04超声波模块 3、震动传感器 4、蜂鸣器 5、若干杜邦线 1、PWM开…

【系统架构设计】操作系统(一)

操作系统&#xff08;一&#xff09; 操作系统的类型和结构操作系统基本原理进程管理进程三态模型挂起状态进程互斥 / 进程同步前趋图进程调度死锁 存储管理设备管理文件管理作业管理 操作系统原理的关键在于“一个观点、两条线索”&#xff1a;一个观点是以资源管理的观点来定…

SpringBoot实用篇

一、运维实用篇 1.工程的打包与运行 ①对SpringBoot项目打包&#xff08;执行Maven构建指令package&#xff09; ②运行项目&#xff08;执行启动指令&#xff09; java -jar 工程打包的文件名.jar jar支持命令行启动需要依赖maven插件支持&#xff0c;请确认打包时是否具有…