CAS(Compare And Swap)

目录

CAS概念

乐观锁与悲观锁

ABA问题

Unsafe类

​编辑

原子类

基本类型原子类

原子引用类

原子数组

原子更新器类

原子累加器


CAS概念

CAS是Compare And Swap的缩写,中文翻译成:比较并交换,实现无锁并发时常用到的一种技术。它一般包含三个操作数——内存位置(V)、预期原值(A)及更新值(B)。某线程执行CAS操作的时候,将内存位置的值与预期原值比较:

  • 如果相匹配,那么处理器会自动将该位置值更新为新值
  • 如果不匹配,那么重新获取该内存位置的值,然后线程进行自旋,到下次循环才有机会执行。

 

底层原理:CAS 是CPU并发原语,底层是 lock cmpxchg 指令(X86 架构),在单核和多核 CPU 下都能够保证比较交换的原子性。

  • 程序是在单核处理器上运行,会省略 lock 前缀,单处理器自身会维护处理器内的顺序一致性,不需要 lock 前缀的内存屏障效果

  • 程序是在多核处理器上运行,会为 cmpxchg 指令加上 lock 前缀。当某个核执行到带 lock 的指令时,CPU 会执行总线锁定或缓存锁定,将修改的变量写入到主存,这个过程不会被线程的调度机制所打断,保证了多个线程对内存操作的原子性

CAS 特点:

  • CAS 体现的是无锁并发、无阻塞并发,线程不会陷入阻塞,线程不需要频繁切换状态(上下文切换,系统调用,用户态转换为内核态)

  • CAS 是基于乐观锁的思想

CAS 缺点:

  • 执行的是循环操作,如果比较不成功一直在循环,最差的情况某个线程一直取到的值和预期值都不一样,就会无限循环导致饥饿,使用 CAS 线程数不要超过 CPU 的核心数,采用分段 CAS 和自动迁移机制

  • 只能保证一个共享变量的原子操作

    • 对于一个共享变量执行操作时,可以通过循环 CAS 的方式来保证原子操作

    • 对于多个共享变量操作时,循环 CAS 就无法保证操作的原子性,这个时候只能用锁来保证原子性

  • 引出来 ABA 问题

乐观锁与悲观锁

悲观锁:

悲观锁就是我们常说的Synchronized 锁。对于悲观锁来说,它总是认为每次访问共享资源时会发生竞争,所以必须对每次对共享资源的操作加上锁,以保证临界区的程序同一时间只能有一个线程在执行。

乐观锁:

乐观锁又称为“无锁”,顾名思义,它是乐观派。乐观锁总是假设对共享资源的访问没有竞争的,线程可以不停地执行,无需加锁也无需等待。 乐观锁的一种实现方式 CAS 实现的 。

ABA问题

当一个主线程获取主内存值时,该内存值在写入主内存时已经被修改了 N 次,但是最终又改成原来的值,比如其他线程先把 A 改成 B 又改回 A,主线程使用CAS操作仅能判断出共享变量的值与最初值 A 是否相同,不能感知到这种从 A 改为 B 又 改回 A 的情况,这时 CAS 虽然成功,但是过程存在问题。

解决方法:使用AtomicStampedReference(加版本号解决ABA问题)

  • 构造方法:

    • public AtomicStampedReference(V initialRef, int initialStamp)

    • initialRef:初始值

    • initialStamp:初始版本号

  • 常用API:

  • public boolean compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp)expectedReference:预期值、newReference:修改值 expectedStamp:期望版本号、expectedReference:操作成功之后的版本号。
  • public void set(V newReference, int newStamp):设置值和版本号

  • public V getReference():返回引用的值

  • public int getStamp():返回当前版本号

public static void main(String[] args) {AtomicStampedReference<Integer> atomicReference = new AtomicStampedReference<>(100,1);int startStamp = atomicReference.getStamp();new Thread(() ->{int stamp = atomicReference.getStamp();atomicReference.compareAndSet(100, 101, stamp, stamp + 1);stamp = atomicReference.getStamp();atomicReference.compareAndSet(101, 100, stamp, stamp + 1);},"t1").start();
​new Thread(() ->{try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}if (!atomicReference.compareAndSet(100, 200, startStamp, startStamp + 1)) {System.out.println(atomicReference.getReference());//100System.out.println(Thread.currentThread().getName() + "线程修改失败");}},"t2").start();
}

执行结果:

100
t2线程修改失败

Unsafe类

Unsafe 是 CAS 的核心类,由于 Java 无法直接访问底层系统,需要通过本地(Native)方法来访问

Unsafe 类存在 sun.misc 包,其中所有方法都是 native 修饰的,都是直接调用操作系统底层资源执行相应的任务,基于该类可以直接操作特定的内存数据,其内部方法操作类似 C 的指针

可以看到AtomicInteger底层调用的就是Unsafe类中的compareAndSwapInt()方法

原子类

基本类型原子类

常用API

常见原子类:AtomicInteger、AtomicBoolean、AtomicLong

以AtomicInteger为例进行介绍

构造方法:

  • public AtomicInteger():初始化一个默认值为 0 的原子型 Integer

  • public AtomicInteger(int initialValue):初始化一个指定值的原子型 Integer

常用API:

方法作用
public final int get()获取 AtomicInteger 的值
public final int getAndIncrement()以原子方式将当前值加 1,返回的是自增前的值
public final int incrementAndGet()以原子方式将当前值加 1,返回的是自增后的值
public final int getAndSet(int value)以原子方式设置为 newValue 的值,返回旧值
public final int addAndGet(int data)以原子方式将输入的数值与实例中的值相加并返回 实例:AtomicInteger 里的 value

原子引用类

原子引用:对 Object 进行原子操作,提供一种读和写都是原子性的对象引用变量

原子引用类:AtomicReference、AtomicStampedReference、AtomicMarkableReference

AtomicReference 类:

  • 构造方法:AtomicReference<T> atomicReference = new AtomicReference<T>()

  • 常用 API:

    • public final boolean compareAndSet(V expectedValue, V newValue):CAS 操作

    • public final void set(V newValue):将值设置为 newValue

    • public final V get():返回当前值

public class AtomicReferenceDemo {public static void main(String[] args) {Student s1 = new Student(33, "z3");// 创建原子引用包装类AtomicReference<Student> atomicReference = new AtomicReference<>();// 设置主内存共享变量为s1atomicReference.set(s1);
​// 比较并交换,如果现在主物理内存的值为 z3,那么交换成 l4while (true) {Student s2 = new Student(44, "l4");if (atomicReference.compareAndSet(s1, s2)) {break;}}System.out.println(atomicReference.get());}
}
​
class Student {private int id;private String name;//。。。。
}

原子数组

原子数组类:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray

常用API:(以AtomicIntegerArray)

  • addAndGet(int i, int delta):以原子更新的方式将数组中索引为i的元素与输入值相加;
  • getAndIncrement(int i):以原子更新的方式将数组中索引为i的元素自增加1;
  • compareAndSet(int i, int expect, int update):将数组中索引为i的位置的元素进行更新
public static void main(String[] args) {AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(new int[5]);int t = 0;t = atomicIntegerArray.getAndSet(0,1);System.out.println(t+"\t"+atomicIntegerArray.get(0)); //0	1atomicIntegerArray.getAndIncrement(1);atomicIntegerArray.getAndIncrement(1);t = atomicIntegerArray.getAndIncrement(1);System.out.println(t+"\t"+atomicIntegerArray.get(1));//2	3}

原子更新器类

原子更新器类:AtomicReferenceFieldUpdater、AtomicIntegerFieldUpdater、AtomicLongFieldUpdater

利用字段更新器,可以针对对象的某个域(Field)进行原子操作,只能配合 volatile 修饰的字段使用,否则会出现异常 IllegalArgumentException: Must be volatile type

常用 API:

  • static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> c, String fieldName):构造方法

  • abstract boolean compareAndSet(T obj, int expect, int update):CAS

public class UpdateDemo {private volatile int field;public static void main(String[] args) {AtomicIntegerFieldUpdater fieldUpdater = AtomicIntegerFieldUpdater.newUpdater(UpdateDemo.class, "field");UpdateDemo updateDemo = new UpdateDemo();fieldUpdater.compareAndSet(updateDemo, 0, 10);System.out.println(updateDemo.field);//10}
}

原子累加器

原子累加器类:LongAdder、DoubleAdder、LongAccumulator、DoubleAccumulator

LongAdder 和 LongAccumulator 区别:

相同点:

  • LongAddr 与 LongAccumulator 类都是使用非阻塞算法 CAS 实现的

  • LongAddr 类是 LongAccumulator 类的一个特例,只是 LongAccumulator 提供了更强大的功能,可以自定义累加规则,当accumulatorFunction 为 null 时就等价于 LongAddr

不同点:

  • 调用 casBase 时,LongAccumulator 使用 function.applyAsLong(b = base, x) 来计算,LongAddr 使用 casBase(b = base, b + x)

  • LongAccumulator 类功能更加强大,构造方法参数中

    • accumulatorFunction 是一个双目运算器接口,可以指定累加规则,比如累加或者相乘,其根据输入的两个参数返回一个计算值,LongAdder 内置累加规则

    • identity 则是 LongAccumulator 累加器的初始值,LongAccumulator 可以为累加器提供非0的初始值,而 LongAdder 只能提供默认的 0

常用API:

  • add():将当前value加x
  • increment():将当前的value加1
  • decrement():将当前的value减1
  • sum():放回当前值。注意:在没有并发更新value的情况下,sum会返回一个精确值,在存在并发的情况下,sum不保证返回精确值。
  • reset():将value重置为0,可用来代替重新new一个LongAdder,单词方法只可以在没有并发更新的情况下使用。
public static void main(String[] args) {LongAdder longAdder = new LongAdder();//0longAdder.increment();longAdder.increment();longAdder.increment();System.out.println(longAdder.longValue());// 3//初始化的时候传入自定义函数操作LongAccumulator longAccumulator = new LongAccumulator((x, y) -> x * y,2);// 2longAccumulator.accumulate(1);//2longAccumulator.accumulate(2);//4longAccumulator.accumulate(3);//12System.out.println(longAccumulator.longValue());// 12}

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

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

相关文章

element plus的el-image图片发布到nginx不显示

问题&#xff1a; <el-image alt""src"/img/month-b.png" class"card-icon"style"width: 89px;height: 89px;right: -7px;top: -5px;"/> 部署到nginx二级路由访问地址是&#xff1a; http://192.168.1.207/divided/# 这时候使用…

总结jvm中GC机制(垃圾回收)

前言 本篇博客博主将介绍jvm中的GC机制&#xff0c;坐好板凳发车啦~~ 一.GC相关 1.1回收栈内存 对于虚拟机栈&#xff0c;本地方法栈这部分区域而言&#xff0c;其生命周期与相关线程相关&#xff0c;随线程而生&#xff0c;随线程而灭。并且这三个区域的内存分配与回收具有…

OpenHarmony:全流程讲解如何编写ADC平台驱动以及应用程序

ADC&#xff08;Analog to Digital Converter&#xff09;&#xff0c;即模拟-数字转换器&#xff0c;可将模拟信号转换成对应的数字信号&#xff0c;便于存储与计算等操作。除电源线和地线之外&#xff0c;ADC只需要1根线与被测量的设备进行连接。 一、案例简介 该程序是基于…

github本地仓库push到远程仓库

1.从远程仓库clone到本地 2.生成SSH秘钥&#xff0c;为push做准备 在Ubuntu命令行输入一下内容 [rootlocalhost ~]# ssh-keygen -t rsa < 建立密钥对&#xff0c;-t代表类型&#xff0c;有RSA和DSA两种 Generating public/private rsa key pair. Enter file in whi…

Synchronized锁升级过程

无锁-->偏向锁---> 轻量级锁---->重量级锁 ①、从无锁到偏向锁&#xff1a; 当一个线程首次访问同步块时&#xff0c;如果此对象无锁状态且偏向锁未被禁用&#xff0c;JVM 会将该对象头的锁标记改为偏向锁状态&#xff0c;并记录下当前线程的 ID。此时&#xff0c;对…

Java进阶-反射的详解与应用

本文深入探讨了Java反射机制的核心概念、应用实例及其在现代Java开发中的重要性。文章首先介绍了反射的基本原理和能力&#xff0c;包括在运行时动态获取类信息、操作对象字段和方法的能力。随后&#xff0c;通过具体代码示例&#xff0c;展示了如何利用反射进行字段访问、方法…

3.5网安学习第三阶段第五周回顾(个人学习记录使用)

本周重点 ①SSRF服务器端请求伪造 ②序列化和反序列化 ③Vaudit代码审计 本周主要内容 ①SSRF服务器端请求伪造 一、概述 SSRF: server site request forgery (服务器端请求伪造)。 SSR: 服务端请求&#xff0c;A服务器通过函数向B服务器发送请求。 SSRF发生的前提条件…

Linux:入门篇

文章目录 前言1. Linuxd的安装环境2.Linux的简单介绍2.1 新建目录2.2 新建文件 3.指令到底是什么&#xff1f;4.shell命令以及运行原理5.总结 前言 很多人对于Linux的学习总是感觉无法下手&#xff0c;不知道从何开始学习&#xff0c;相信这篇文章将会为你提供一个清晰的思路。…

高精度算法(加、减、乘、除,使用c++实现)

一、概念 在我们进行计算的过程中&#xff0c;经常会遇到几十位&#xff0c;甚至几百位的数字的计算问题&#xff0c;也有可能会遇到小数点后几十位&#xff0c;几百位的情况&#xff0c;而我们面对这样的情况下&#xff0c; 和 的数据范围显然是不够使用的了。因此这时&am…

Node.js-知识点学习总结归纳

Node.js-知识点学习总结归纳 安装nodenode运行方式通过Node.js直接运行js文件&#xff08;也就不用通过网页html了&#xff09;绝对路径调用:相对路径调用&#xff1a;直接运行js命令&#xff1a; Vscode控制台使用node运行js文件 安装node 这个就不用讲了吧&#xff0c;网上搜…

开源知识库平台Raneto--使用Docker部署Raneto

文章目录 一、Raneto介绍1.1 Raneto简介1.2 知识库介绍 二、阿里云环境2.1 环境规划2.2 部署介绍 三、环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本 四、下载Raneto镜像五、部署Raneto知识库平台5.1 创建挂载目录5.2 编辑config.js文件5.3 编…

Sui Basecamp日程公布,两天超50场密集分享等你来参加

随着4月的来临&#xff0c;我们也怀着激动的心情迎来了Sui全球旗舰品牌会议Sui Basecamp的个位数倒计时。 Sui Basecamp将在4月10–11日巴黎区块链周期间举行&#xff0c;Web3构建者、知名企业和信仰者齐聚一堂&#xff0c;在这里共同创造、学习和建立联系。Basecamp将由具有对…

左值与右值,以及c++11的相关特性。

目录 左值 右值 左值引用总结&#xff1a; 右值引用总结&#xff1a; 右值引用使用场景和意义&#xff1a; 1、左值引用的使用场景&#xff1a; 编译器优化1&#xff1a; 2、移动构造与移动赋值&#xff1a; 3、右值引用的使用场景&#xff1a; 编译器优化2&#xff1a…

excel匹配替换脱敏身份证等数据

假如excel sheet1中有脱敏的身份证号码和姓名&#xff0c;如&#xff1a; sheet2中有未脱敏的数据数据 做法如下&#xff1a; 1、在sheet2的C列用公式 LEFT(A2,6)&REPT("*",8)&RIGHT(A2,4) 做出脱敏数据&#xff0c;用来与sheet1的脱敏数据匹配 2、在sheet…

【卫星家族】 | 高分六号卫星影像及获取

1. 卫星简介 高分六号卫星&#xff08;GF-6&#xff09;于2018年6月2日在酒泉卫星发射中心成功发射&#xff0c;是高分专项中的一颗低轨光学遥感卫星&#xff0c;也是我国首颗精准农业观测的高分卫星&#xff0c;具有高分辨率、宽覆盖、高质量成像、高效能成像、国产化率高等特…

Mysql数据库:故障分析与配置优化

目录 前言 一、Mysql逻辑架构图 二、Mysql单实例常见故障 1、无法通过套接字连接到本地MySQL服务器 2、用户rootlocalhost访问被拒绝 3、远程连接数据库时连接很慢 4、无法打开以MYI结尾的索引文件 5、超出最大连接错误数量限制 6、连接过多 7、配置文件/etc/my.cnf权…

python 哔哩哔哩视频去水印

使用python 去除视频中的水印 1. 需要安装的包 pip install moviepy pip install numpy pip install opencv_python pip install tqdm 2. 代码 import cv2 import numpy as np import glob from moviepy.editor import VideoFileClip import os from tqdm import tqdm# 判…

2024 年每个程序员都应该尝试的 8 个AI工具

随着人工智能技术的极速发展&#xff0c;新的 AI 工具正以前所未有的速度涌现&#xff0c;为开发者们带来了前所未有的机会和挑战。在这个不断演进的时代&#xff0c;掌握最新的 AI 技术已成为每个程序员的必修课。 在本文中&#xff0c;我们收集了8 个程序员在 2024 年值得尝…

idea 报错 Could not list the contents of folder “ftps

idea 报错 Could not list the contents of folder "ftps 解决方案 这里看到了网上的解决方案&#xff0c;顺便再记录一下。打开 【高级】菜单 - 取消勾选 被动模式。然后点击测试连接&#xff0c;显示连接成功&#xff01; ftp中的主动模式和被动模式 主动模式&…

《HelloGitHub》第 96 期

兴趣是最好的老师&#xff0c;HelloGitHub 让你对编程感兴趣&#xff01; 简介 HelloGitHub 分享 GitHub 上有趣、入门级的开源项目。 https://github.com/521xueweihan/HelloGitHub 这里有实战项目、入门教程、黑科技、开源书籍、大厂开源项目等&#xff0c;涵盖多种编程语言 …