Java——》CAS

推荐链接:
    总结——》【Java】
    总结——》【Mysql】
    总结——》【Redis】
    总结——》【Kafka】
    总结——》【Spring】
    总结——》【SpringBoot】
    总结——》【MyBatis、MyBatis-Plus】
    总结——》【Linux】
    总结——》【MongoDB】
    总结——》【Elasticsearch】

Java——》CAS

  • 一、概念
  • 二、参数
  • 三、结果
  • 四、使用场景
  • 五、底层实现
    • 1、Java:Unsafe类中的native方法
    • 2、C++:`unsafe.cpp中的`Unsafe_CompareAndSwapInt执行cmpxchg指令
    • 3、linux:atomic_linux_x86.inline.hpp中的cmpxchg使用lock指令
  • 六、基于CAS实现的类
  • 七、优点
  • 八、缺点
    • 1、ABA问题
      • (1)现象
      • (2)解决方案
      • (3)示例
    • 2、如果cas失败(自旋)次数过多占用CPU资源
      • (1)现象
      • (2)解决方案
    • 3、只能保证一个数据的安全
      • (1)现象
      • (2)解决方案
  • 九、CPU 实现原子指令
    • 1、通过总线锁定来保证原子性
    • 2、通过缓存锁定来保证原子性
  • 十、CAS保证原子性示例

一、概念

CAS = Compare And Swap = 比较并交换

  • 先比较一下值是否与预期值一致,如果一致,交换,返回true
  • 先比较一下值是否与预期值一致,如果不一致,不交换,返回false

    CAS是一种在多线程环境中进行操作的原子指令。它的作用是对内存中的某个位置进行一次“乐观”的读取,并根据读取值与期望值是否相同来决定是否对该内存位置进行更新。
    CAS 操作是一种无锁的写入方式,允许多个线程并发的访问内存,并且通常比加锁操作的性能要高。
    Java中基于Unsafe的类提供了对CAS的操作的方法,JVM会帮助我们将方法实现CAS汇编指令。
    通过cmpxchg指令实现,单核就采用cmpxchg,多核需要追加前缀lock指令保证只有一个线程在执行当前CAS。

二、参数

参数描述
内存位置(V)要进行操作的内存位置
期望值(E)将内存位置的值与期望值进行比较,如果相同则进行更新,否则不进行操作
更新值(U)当期望值与内存位置的值相同时,CAS 操作会将内存位置的值更新为更新值

三、结果

CAS 操作的结果是一个布尔值,表示操作是否成功。
当期望值与内存位置的值相同时,CAS 操作会成功并返回 true,否则返回 false。

四、使用场景

  1. 实现各种无锁的同步机制,例如计数器、队列和栈等。
  2. 线程数较少、等待时间短可以采用自旋锁进行CAS尝试拿锁,较于synchronized高效

五、底层实现

最终回答:先从比较和交换的角度去聊清楚,在Java端聊到native方法,然后再聊到C++中的cmpxchg的指令,再聊到lock指令保证cmpxchg原子性

1、Java:Unsafe类中的native方法

CAS在Java层面就是Unsafe类中提供的一个native方法,成功返回true,失败返回false,如果需要重试策略,自己实现。

native是直接调用本地依赖库C++中的方法

/`* 如果变量的值为预期值,则更新变量的值,该操作为原子操作,如果修改成功则返回true* * 4个参数:哪个对象,哪个属性的内存偏移量,oldValue预期值,newValue最终值*/
public final native boolean compareAndSwapObject(Object o, long offset,Object expected,Object x);public final native boolean compareAndSwapInt(Object o, long offset,int expected,int x);public final native boolean compareAndSwapLong(Object o, long offset,long expected,long x);

2、C++:unsafe.cpp中的Unsafe_CompareAndSwapInt执行cmpxchg指令

本地已经下载hospot源码:\hotspot\src\share\vm\prims\unsafe.cpp
直接官网查看hospot源码:http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473/src/share/vm/prims/unsafe.cpp

UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))UnsafeWrapper("Unsafe_CompareAndSwapInt");oop p = JNIHandles::resolve(obj);jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
UNSAFE_END

image.png

3、linux:atomic_linux_x86.inline.hpp中的cmpxchg使用lock指令

本地已经下载hospot源码:\hotspot\src\os_cpu\linux_x86\vm\atomic_linux_x86.inline.hpp
直接官网查看hospot源码:http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473/src/os_cpu/linux_x86/vm/atomic_linux_x86.inline.hpp

cmpxchg是汇编指令,CPU硬件底层就支持cmpxchg。
cmpxchgl本身是原子性操作(不能再拆分的指令),但并不能保证原子性,所以需要判断当前系统是否为多核处理器,如果是多核,就添加lock前缀。
lock指令可以理解为CPU层面的锁,一般锁的粒度就是缓存行级别的锁,当然也有总线锁,但是成本太高,CPU会根据情况选择。

inline jint     Atomic::cmpxchg    (jint     exchange_value, volatile jint*     dest, jint     compare_value) {int mp = os::is_MP();// 内联函数,用来判断当前系统是否为多处理器(如果当前系统是多处理器返回1,否则返回0)
// __asm__代表汇编,直接操作硬件,执行cmpxchgl指令
__asm__ volatile (LOCK_IF_MP(%4) "cmpxchgl %1,(%3)" // 如果当前系统是多处理器(即mp值为1),则为cmpxchg指令添加lock前缀,否则不加lock前缀: "=a" (exchange_value): "r" (exchange_value), "a" (compare_value), "r" (dest), "r" (mp): "cc", "memory");return exchange_value;
}

image.png

六、基于CAS实现的类

参考链接:
Java——》Unsafe源码分析
Java——》AtomicInteger源码分析

  • AtomicInteger
  • LongAdder
  • ReentrantLock

image.png

七、优点

什么是非阻塞式的呢?其实就是一个线程想要获得锁,对方会给一个回应表示这个锁能不能获得。在资源竞争不激烈的情况下性能高,相比synchronized重量锁会进行比较复杂的加锁,解锁和唤醒操作。

CAS是一种乐观锁,而且是一种非阻塞的轻量级的乐观锁,不会挂起线程。

八、缺点

1、ABA问题

ABA不一定是问题!因为一些只存在 ++,–的这种操作,即便出现ABA问题,也不影响结果!

(1)现象

image.png

(2)解决方案

在修改value同时追加 版本号

(3)示例

JUC下提供的AtomicStampedReference(同时判断值和版本号)
image.png

public static void main(String[] args) {AtomicStampedReference<String> reference = new AtomicStampedReference<>("AAA",1);String oldValue = reference.getReference();int oldVersion = reference.getStamp();boolean b = reference.compareAndSet(oldValue, "B", oldVersion, oldVersion + 1);System.out.println("修改1版本的:" + b);boolean c = reference.compareAndSet("B", "C", 1, 1 + 1);System.out.println("修改2版本的:" + c);
}

2、如果cas失败(自旋)次数过多占用CPU资源

(1)现象

while循环,cas一直没成功,会一直进行自旋,会额外的占用大量的CPU资源
image.png

Q:为什么会占用CPU资源?
A:因为CAS不会挂起线程,会让CPU一致调度当前线程执行CAS直到成功。

(2)解决方案

不同场景有不同的处理方案

方案特点具体实现
synchronized的实现方式自适应自旋锁如果cas失败(自旋)次数过多(超过指定次数),就挂起线程(WAITING),避免占用CPU过多的资源
LongAdder的实现方式分段锁在CAS一次失败后,将这个操作暂存起来,后面需要获取结果时,将暂存的操作全部执行,再返回最后的结果

Q:LongAdder?
A:基于类似 分段锁 的形式去解决(要看业务,有限制的)
传统的AtmoicLong是针对内存中唯一的一个值去++
现在的LongAdder在内存中搞了好多个值,多个线程去加不同的值,如果自增失败,将失败的信息添加到Cell[],当需要结果时,将所有值累加再返回。

3、只能保证一个数据的安全

(1)现象

无法像synchronized一样锁住一段代码

(2)解决方案

ReentrantLock基于AQS实现,AQS基于CAS的方式实现了锁的效果。

九、CPU 实现原子指令

CPU 实现原子指令有 2 种方式:

  • 1、通过总线锁定来保证原子性
  • 2、通过缓存锁定来保证原子性

1、通过总线锁定来保证原子性

所谓总线锁定就是使用处理器提供的一个 LOCK# 信号,当一个处理器在总线上输出此信号时,其他处理器的请求将被阻塞住,那么该处理器可以独占共享内存。但是该方法成本太大。因此有了下面的方式。

2、通过缓存锁定来保证原子性

所谓 缓存锁定 是指内存区域如果被缓存在处理器的缓存行中,并且在 Lock 操作期间被锁定,那么当他执行锁操作写回到内存时,处理器不在总线上声言 LOCK# 信号,而是修改内部的内存地址,并允许他的缓存一致性机制来保证操作的原子性,因为缓存一致性机制会阻止同时修改两个以上处理器缓存的内存区域数据(这里和 volatile 的可见性原理相同),当其他处理器回写已被锁定的缓存行的数据时,会使缓存行无效。

注意:有两种情况下处理器不会使用缓存锁定:

  1. 当操作的数据不能被缓存在处理器内部,或操作的数据跨多个缓存行时,则处理器会调用总线锁定。
  2. 有些处理器不支持缓存锁定,对于 Intel 486 和 Pentium 处理器,就是锁定的内存区域在处理器的缓存行也会调用总线锁定。

十、CAS保证原子性示例

private static AtomicInteger count = new AtomicInteger(0);public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {for (int i = 0; i < 100; i++) {count.incrementAndGet();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 100; i++) {count.incrementAndGet();}});t1.start();t2.start();t1.join();t2.join();System.out.println(count);
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

伊朗网络间谍组织针对中东金融和政府部门

导语 近日&#xff0c;以色列网络安全公司Check Point与Sygnia发现了一起针对中东金融、政府、军事和电信部门的网络间谍活动。这一活动由伊朗国家情报和安全部门&#xff08;MOIS&#xff09;支持的威胁行为者发起&#xff0c;被称为"Scarred Manticore"。该组织被认…

世微 dc-dc同步降压 IC AP3466 车载充电器适配器恒压源 电源驱动

产品描述 AP3466 是一款支持宽电压输入的同步降压 电源管理芯片&#xff0c;输入电压 4-30V 范围内可实现 3.6A 的连续电流输出。通过调节 FB 端口的分压 电阻&#xff0c;设定输出 1.8V 到 28V 的稳定电压。 AP3466 具有优秀的恒压/恒流(CC/CV)特性。 AP3466 采用电流模式的环…

03、SpringBoot + 微信支付 ---- 创建订单、保存二维码url、显示订单列表

目录 Native 下单1、创建课程订单保存到数据库1-1&#xff1a;需求&#xff1a;1-2&#xff1a;代码&#xff1a;1-3&#xff1a;测试结果&#xff1a; 2、保存支付二维码的url2-1&#xff1a;需求&#xff1a;2-2&#xff1a;代码&#xff1a;2-3&#xff1a;测试&#xff1a;…

【WinForm详细教程七】WinForm中的DataGridView控件

文章目录 1.主要属性DataSource行&#xff08;Row 相关属性&#xff09;列&#xff08;Column 相关属性&#xff09;单元格&#xff08;Cell 相关属性&#xff09;逻辑删除AllowUserToAddRowsAllowUserToDeleteRowsAllowUserToOrderColumns其他布局和行为属性 2.控件中的行、列…

【Linux进程】进程地址空间

目录 程序地址空间回顾 进程地址空间 宏观理解 谈细节 1、进程地址空间究竟是什么&#xff1f; 2、管理地址空间 3、页表 总结几个问题&#xff1a; 1、为什么要有进程地址空间&#xff1f; 2、进程切换 3、进程具有独立性&#xff0c;怎么做到的&#xff1f; 程序地…

axios 全局错误处理和请求取消

这两个功能都是用拦截器实现。 前景提要&#xff1a; ts 简易封装 axios&#xff0c;统一 API 实现在 config 中配置开关拦截器 全局错误处理 在构造函数中&#xff0c;添加一个响应拦截器即可。在构造函数中注册拦截器的好处是&#xff0c;无论怎么实例化封装类&#xff0c…

改进的yolov5

The networkstructure of these models is constant, but the modules and con-volution kernels are scaled, which alters the complexity and sizeof each model.&#xff08;这些模型的网络结构是恒定的&#xff0c;但模块和卷积核被缩放&#xff0c;这改变了每个模型的复杂…

node版本管理神器|nvm安装使用教程

文章目录 导文安装前提下载安装包进行安装第一步&#xff0c;下载安装包&#xff0c;[nvm下载地址](https://github.com/coreybutler/nvm-windows/releases)第二步&#xff0c;进行安装&#xff0c;点击nvm-setup.exe文件&#xff0c;接受用户协议第三步&#xff0c;选择安装目…

​Z时代时尚SUV新宠:起亚赛图斯值不值得年轻人买?

在当今汽车行业中&#xff0c;随着消费者偏好的多样化和年轻化&#xff0c;汽车制造商们正面临着前所未有的挑战与机遇。在2023年上海车展上&#xff0c;起亚汽车公司正式发布了全新紧凑级SUV——赛图斯。这款车型不仅标志着起亚对年轻消费市场的深入洞察&#xff0c;也展现了公…

Chatgpt批量改写文章网页版可多开软件-自动登录换号生成word或者TXT

Chatgpt批量改写文章网页版可多开软件介绍&#xff1a; 1、改写后生成docx格式文档和生成txt文档二选一。 2、支持原来docx文档里带图片的改写&#xff0c;改写伪原创后的docx里也带图片。 3、软件可以设置是否开启标题改写&#xff0c;可以自定义标题改写指令。 4、可以设置…

CVE-2023-34040 Kafka 反序列化RCE

漏洞描述 Spring Kafka 是 Spring Framework 生态系统中的一个模块&#xff0c;用于简化在 Spring 应用程序中集成 Apache Kafka 的过程&#xff0c;记录 (record) 指 Kafka 消息中的一条记录。 受影响版本中默认未对记录配置 ErrorHandlingDeserializer&#xff0c;当用户将容…

2023 现阶段H5的机型适配

个人愚见 现在的主流体验&#xff0c;是大屏手机展示更多的内容&#xff0c;并不着重于放大展示&#xff0c; 所以&#xff0c;外层布局使用vw,百分比&#xff0c;flex&#xff0c;内层直接px就行 比如微信服务页面&#xff0c;大屏下展示更多数据

海外服务器和国内的几大差别!如何选择一款优质的海外服务器?

随着互联网的普及&#xff0c;越来越多的企业或者个人都在搭建自己的网站&#xff0c;而对于搭建网站来说&#xff0c;选择一款合适的服务器显得至关重要。那么许多小伙伴可能都在纠结的一个问题是&#xff0c;海外服务器和国内服务器有什么区别&#xff1f;我们是否应该选择一…

http中的Content-Type类型

浏览器的Content-Type 最近在做web端下载的时候需要给前端返回一个二进制的流&#xff0c;需要在请求头中设置一个 writer.Header().Set("Content-Type", "application/octet-stream")那么http中的Content-Type有具体有哪些呢&#xff1f;他们具体的使用场…

消息中间件-RabbitMQ介绍

一、基础知识 1. 什么是RabbitMQ RabbitMQ是2007年发布&#xff0c;是一个在AMQP(高级消息队列协议)基础上完成的&#xff0c;简称MQ全称为Message Queue, 消息队列&#xff08;MQ&#xff09;是一种应用程序对应用程序的通信方法&#xff0c;由Erlang&#xff08;专门针对于大…

HarmonyOS 数据管理与应用数据持久化(二)

通过键值型数据库实现数据持久化 场景介绍 键值型数据库存储键值对形式的数据&#xff0c;当需要存储的数据没有复杂的关系模型&#xff0c;比如存储商品名称及对应价格、员工工号及今日是否已出勤等&#xff0c;由于数据复杂度低&#xff0c;更容易兼容不同数据库版本和设备…

win中安装nvm进行Node版本控制

之前有安装node.js安装包的需要先给卸载掉先通过官网下载安装包nvm-setup.zip nvm官网地址命令行打开输入nvm -v&#xff0c;测试安装是否成功 此时如果进行node相关版本安装的话&#xff0c;可能下载速度过慢&#xff0c;此时需要更换镜像源。 斜体样式找到nvm的安装路径 &am…

Java智慧工地管理平台可视化大数据建造工地APP源码

建筑行业是国民经济的重要物质生产部门和支柱产业之一&#xff0c;同时&#xff0c;建筑业也是一个安全事故多发的高危行业。如何加强施工现场安全管理、降低事故发生频率、杜绝各种违规操作和不文明施工、提高建筑工程质量&#xff0c;是摆在各级政府部门、施工企业面前的一道…

CRM软件如何高效培育销售线索?

​ 通过线索培育可以挖掘出更多CRM软件销售管道中的有价值客户提高销售业绩。但机遇与挑战总是共存的&#xff0c;培育线索要从不同的渠道执行大量重复性的操作&#xff0c;人为操控不仅速度慢而且容易出错&#xff0c;那么企业如何高效培育销售线索? 发送个性化邮件 我们知…

时间序列预测模型实战案例(八)(Informer)个人数据集、详细参数、代码实战讲解

论文地址->Informer论文地址PDF点击即可阅读 代码地址-> 论文官方代码地址点击即可跳转下载GIthub链接 本文介绍 本篇博客带大家看的是Informer模型进行时间序列预测的实战案例&#xff0c;它是在2019年被提出并在ICLR 2020上被评为Best Paper&#xff0c;可以说Inform…