CAS是什么

article/2025/9/29 15:59:28

目录

没有CAS之前

使用CAS之后

CAS是什么

CAS底层原理:unsafe类

Unsafe

new AtomicInteger().getAndIncrement()流程

CAS缺点

1 循环时间长开销很大

2 ABA问题

ABA代码演示 

如何解决?:通过AtomicStampedReference版本号

3不能保证代码块的原子性 

自定义原子引用

自旋锁(spinlock)

通过CAS实现手自旋锁


没有CAS之前

常用synchronized锁保证线程安全i++,但是它比较重 ,牵扯到了用户态和内核态的切换,效率不高。

使用CAS之后

类似于乐观锁保证线程安全i++

CAS是什么

CAS是英文单词Compare And Swap的缩写,翻译过来就是比较并替换。

CAS机制当中使用了3个基本操作数:内存地址V旧的预期值A要修改的新值B

更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改为B。否则进行自选重新获取。

CAS底层原理:unsafe类

compareAndSet调用的是unsafe中的本地方法


 

Unsafe

CAS这个理念 ,落地就是Unsafe类

  • 注意Unsafe类中的所有方法都是native修饰的,也就是说Unsafe类中的方法都直接调用操作系统底层资源故行相应任务

它是CAS的核心类,由于Java方法无法直接访问底层系统,需要通过本地(native)方法来访问,Unsafe相当于一个后门 ,基于该类可以直接操作特定内存\ 的数据 。Unsafe类存在于sun.misc包中,其内部方法操作可以像C的指针一样直接操作内存,因为Java中CAS操作的执行依赖于Unsafe类的方法。
 

变量valueOffset,表示该变量值在内存中的偏移地址,因为Unsafe就是根据内存偏移地址获取数据的。 

变量value用volatile修饰,保证可见性

CAS的全称为Compare-And-Swap,它是一条CPU并发原语。
它的功能是判断内存某个位置的值是否为预期值,如果是则更改为新的值,这个过程是原子的。
AtomicInteger 类主要利用 CAS (compare and swap) + volatile 和 native 方法来保证原子操作,从而避免 synchronized 的高开销,执行效率大为提升。

new AtomicInteger().getAndIncrement()流程

假设线程A和线程B两个线程同时执行getAndAddInt操作(分别跑在不同CPU上): 

1 AtomicInteger里面的value原始值为3,即主内存中AtomicInteger的value为3,根据JMM模型,线程A和线程B各自持有一份值为3的value的副本分别到各自的工作内存。

2 线程A通过getIntVolatile(var1, var2)拿到value值3,这时线程A被挂起。

3 线程B也通过getIntVolatile(var1, var2)方法获取到value值3,此时刚好线程B没有被挂起并执行compareAndSwapInt方法比较内存值也为3,成功修改内存值为4,线程B打完收工,一切OK。

4 这时线程A恢复,执行compareAndSwapInt方法比较,发现自己手里的值数字3和主内存的值数字4不一致,说明该值已经被其它线程抢先一步修改过了,那A线程本次修改失败,只能重新读取重新来一遍了。

5 线程A重新获取value值,因为变量value被volatile修饰,所以其它线程对它的修改,线程A总是能够看到,线程A继续执行compareAndSwapInt进行比较替换,直到成功。

 CAS并发原语体现在JAVA语言中就是sun.misc.Unsafe类中的各个方法。调用UnSafe类中的CAS方法,JVM会帮我们实现出CAS汇编指令 。这是一种完全依赖于硬件的功能,通过它实现了原子操作。再次强调,由于CAS是一种系统原语 ,原语属于操作系统用语范畴,是由若干条指令组成的,用于完成某个功能的一个过程,并且原语的执行必须是连续的,在执行过程中不允许被中断,也就是说CAS是一条CPU的原子指令,不会造成所谓的数据不一致问题。 

CAS缺点

1 循环时间长开销很大

do while如果它一直自旋会一直占用CPU时间,造成较大的开销

如果CAS失败,会一直进行尝试。如果CAS长时间一直不成功,可能会给CPU带来很大的开销。

2 ABA问题

CAS算法实现一个重要前提需要取出内存中某时刻的数据并在当下时刻比较并替换,那么在这个时间差类可能会有数据的变化。

  • 比如说一个线程one从内存位置V中取出A,这时候另一个线程two也从内存中取出A,并且线程two进行了一些操作将值变成了B,
  • 然后线程two又将V位置的数据变成A,这时候线程one进行CAS操作发现内存中仍然是A,然后线程one操作成功。
  • 尽管线程one的CAS操作成功,但是不代表这个过程就是没有问题的。

ABA代码演示 

public class ABADemo {static AtomicInteger atomicInteger = new AtomicInteger(100);public static void main(String[] args) {new Thread(()->{atomicInteger.compareAndSet(100,2022);try { TimeUnit.MICROSECONDS.sleep(200); } catch (InterruptedException e) {e.printStackTrace();}atomicInteger.compareAndSet(2022,100);},"t1").start();new Thread(()->{try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) {e.printStackTrace();}System.out.println(atomicInteger.compareAndSet(100, 2000)+"\t"+atomicInteger.get());},"t2").start();}
}//true	2000

如何解决?:通过AtomicStampedReference版本号

public boolean weakCompareAndSet(V expectedReference,//旧值V newReference,//新值int expectedStamp,//旧版本号int newStamp)//新版本号
以原子方式设置该引用和邮票给定的更新值的值,如果当前的参考是==至预期的参考,并且当前标志等于预期标志。

 代码演示

public class ABADemo {static AtomicInteger atomicInteger = new AtomicInteger(100);static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100,1);public static void main(String[] args) {new Thread(()->{int stamp = atomicStampedReference.getStamp();//初始版本号System.out.println(Thread.currentThread().getName() + "的首次版本号:"+stamp);try { TimeUnit.MICROSECONDS.sleep(200); } catch (InterruptedException e) {e.printStackTrace();}boolean b2 = atomicStampedReference.compareAndSet(100, 101, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);System.out.println(b2+"\t"+Thread.currentThread().getName() + "的2次版本号:"+atomicStampedReference.getStamp() );boolean b3 = atomicStampedReference.compareAndSet(101, 100, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);System.out.println(b3+"\t"+Thread.currentThread().getName() + "的3次版本号:"+atomicStampedReference.getStamp() );},"t1").start();new Thread(()->{int stamp = atomicStampedReference.getStamp();//初始版本号System.out.println(Thread.currentThread().getName() + "的首次版本号:"+atomicStampedReference.getStamp());try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) {e.printStackTrace();}boolean b = atomicStampedReference.compareAndSet(100, 2000, stamp, stamp + 1);System.out.println(b+"\t"+Thread.currentThread().getName() + "的2次版本号"+atomicStampedReference.getStamp());},"t2").start();}t1的首次版本号:1
t2的首次版本号:1
true	t1的2次版本号:2
true	t1的3次版本号:3
false	t2的2次版本号3

3不能保证代码块的原子性 


CAS机制所保证的只是一个变量的原子性操作,而不能保证整个代码块的原子性。比如需要保证3个变量共同进行原子性的更新,就不得不使用Synchronized了。 

自定义原子引用

 代码实现

public class Automic {public static void main(String[] args) {User z3 = new User("z3", 19);User li4 = new User("li4", 22);new AtomicInteger().getAndIncrement();AtomicReference<User> atomicReference = new AtomicReference<>();atomicReference.set(z3);System.out.println(atomicReference.compareAndSet(z3, li4)+"\t"+atomicReference.get().toString());System.out.println(atomicReference.compareAndSet(z3, li4)+"\t"+atomicReference.get().toString());}
}@AllArgsConstructor
@ToString
@Getter
@Setter
class User{String name;int age;
}

 true    User(name=li4, age=22)
false    User(name=li4, age=22)

自旋锁(spinlock)

是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,

当线程发现锁被占用时,会不断循环判断锁的状态,直到获取。

优点:减少线程上下文切换的消耗,

缺点:循环会消耗CPU

通过CAS实现手自旋锁

 自旋锁好处:循环比较获取没有类似wait的阻塞。

  • 通过CAS操作完成自旋锁,t1线程先进来调用myLock方法自己持有锁5秒钟,t2随后进来后发现t1线程持有锁,不是null,所以循环自旋等待,直到t1释放锁后t2随后抢到。

利用AtomicReference.compareAndSet

public class SpinLockDemo {AtomicReference<Thread> atomicReference = new AtomicReference();public void lock(){Thread thread = Thread.currentThread();System.out.println(thread.getName()+"--come in");while (!atomicReference.compareAndSet(null, thread)){//不能修改就一直获取 修改成功不进入循环}}public void unlock(){Thread thread = Thread.currentThread();atomicReference.compareAndSet(thread,null);System.out.println(thread.getName()+"--unlock");}public static void main(String[] args) {SpinLockDemo lockDemo = new SpinLockDemo();new Thread(()->{lockDemo.lock();System.out.println("等待3s");try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}lockDemo.unlock();},"t1").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(()->{lockDemo.lock();lockDemo.unlock();},"t2").start();}}

t1--come in
等待3s
t2--come in
t1--unlock
t2--unlock


http://chatgpt.dhexx.cn/article/edoJxoQr.shtml

相关文章

CAS 是什么?

CAS又称 自旋锁、无锁&#xff0c;是一种乐观锁 compare and swap 的缩写 意为: 比较并交换 , 实现并发算法的常用技术 , 就是说我不用加锁 , 也能保证 ( 加锁会影响效率&#xff0c;可以考虑使用原子操作类 ) 原子性 , 当多个线程尝试使用 CAS 同时更新同一个变量时&#xf…

什么是CAS

文章目录 一、CAS是什么二、CAS 可以解决什么问题三、CAS实现原子操作的问题 一、CAS是什么 CAS的全称为compare and swap 或者compare and exchange,意思为比较和交换。CAS流程如下&#xff1a; 假设我们有一个共享变量i&#xff0c;初始值为0。我们现在要对i进行加1的操作…

metasploit图形化工具 Armitage

Metasploit默认使用PostgreSQL存储渗透测试所需的数据表&#xff0c;所以在启动Armitage之前需要首先启动PostgreSQL服务和Metasploit服务&#xff0c;然后再启动armitage&#xff0c;弹出对话框询问是否连接Metasploit的RPC信道&#xff0c;依次选择“connect”、“是”选项。…

ARM..

一 关于arm 1 arm的三种含义 (1) 一个公司的名称 Advanced RISC Machine (2) 一类处理器的统称 (3) 一种技术的名称 &#xff08;RISC&#xff09; ARM是以一家设计处理器的公司&#xff0c;这家公司设计的处理器统称为ARM&#xff0c;它们使用的指令集是RISC&#xff08;精简指…

01-Introducing the Arm architecture

快速链接: . 👉👉👉 个人博客笔记导读目录(全部) 👈👈👈 付费专栏-付费课程 【购买须知】:【精选】ARMv8/ARMv9架构入门到精通-[目录] 👈👈👈目录 1、Overview2、About the Arm architecture3、架构(architecture)到底是什么意思呢

傻瓜式渗透Armitage的使用

目录 Armitage的基本介绍 安装 1 启动Armitage 1、Armitage启动前初始化&#xff1a; 2、Armitage启动的三种方法&#xff1a; 2 使用Armitage生成被控端和主控端 1、Armitage的工作界面&#xff1a; &#xff08;1&#xff09;区域1&#xff1a; &#xff08;2&#…

Kali Linux Armitage生成被控端和主控端

目录 说明使用 Armitage生成被控端和主控端 说明 按照《Kali Linux2 网络渗透测试实践指南 第二版 》第八章操作 仅供学习讨论使用&#xff0c;请勿进行非法操作 使用 Armitage生成被控端和主控端 选中“payload”&#xff0c; 然后选择“windows” ➡️“meterpreter”&…

[architecture]-arm exclusive机制介绍

关键词:spinlock,原子操作,独占访问,exclusive,inclusive,mutex,Semaphore,互斥,同步,同步原语 快速链接: . &#x1f449;&#x1f449;&#x1f449; 个人博客笔记导读目录(全部) &#x1f448;&#x1f448;&#x1f448; 付费专栏-付费课程 【购买须知】: 【精选】ARMv8/…

使用Armitage进行渗透测试与键盘记录

0x00&#xff1a;Armitage是一款基于GUI开发的图形化渗透工具&#xff0c;对于渗透测试而言能够快速了解网络的拓扑以及主机状态&#xff0c;其功能丰富&#xff0c;是每一位渗透测试者的必备武器&#xff1b;今天我就来演示如何在Armitage下入侵纯在漏洞的XP系统&#xff0c;废…

在2022年的kali linux上面安装armitage

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 在2022年的kali linux上面安装armitage 前言一、armitage是什么&#xff1f;二、安装步骤1.官网下载2.build安装3.配置数据库 总结 前言 提示&#xff1a;在2022年的kali li…

ARMv8 architecture里的Memory aborts

在AArch64 Virtual Memory System Architecture中&#xff0c;有以下几种机制会导致PE在访问memory失败时产生exceptions。 Debug exception: An exception caused by the debug configuration.Alignment fault: An Alignment fault is generated if the address used for a m…

Armitage图形化前端

开源免费图形前端 ● 作者自称是众多不会使用metasploit的安全专家之一&#xff08;命令行&#xff09; ● MSF基于命令行&#xff0c;缺少直观的GUI图形用户接口 Armitage只是调用MSF的漏洞利用能力 ● Armitage的每一个GUI操作都可以对应MSF中的一条命令 红队团队合作模拟…

Kali Linux 使用Armitage

目录 说明安装扫描网络Quick Scan (OS detect)输入网段扫描结果 图形化ms17_010先扫描 auxiliary再攻击被渗透图形化metaspliotMeterpreter 5类任务&#xff1a;计算系统密码的hash值 Browser filesLog Keystrokes 写在最后 Armitage是一款用Java为Metasploit编写的图形化操作界…

【Arm架构】什么是Arm?

1 Arm architecture Arm 架构是世界上最流行的处理器架构之一。每年有数十亿台基于 Arm 的设备出货。 A-Profile (Applications) R-Profile (Real-Time) M-Profile (Microcontroller) 高性能场景 实时系统 小型&#xff0c;高能效设备 用于运行复杂的操作系统&#xff0…

「Arm Arch」 初识 Arm

本文源自《书香度年华》「ARM 架构专栏」&#xff0c;是一系列由浅入深、循序渐进的文章&#xff0c;文章之间有一定的前后关联性&#xff0c;所以按顺序阅读&#xff0c;建议收藏专栏。 目录 前言 一、架构概述 1.1 冯诺依曼架构 1.2 哈佛架构 1.3 Arm 架构 二、架构图谱…

Armitage的介绍和使用

目录 1.1Armitage的介绍与原理 1.1.2 Armitage的主要功能包括&#xff1a; 1.2 启动Armitage 1.2.1 Armitage启动的三种方法&#xff1a; 1.2.2 Armitage启动前初始化&#xff1a; 1.3 使用Armitage生成被控端和主控端 1.3.1 Armitage的工作界面&#xff1a; 1.3.2 使用…

ARM 架构是什么?

ARM&#xff08;Advanced RISC Machines&#xff09;架构是一种处理器架构&#xff0c;它是一种精简指令集计算机&#xff08;RISC&#xff09;架构。ARM架构最初由ARM Holdings&#xff08;现在是SoftBank Group的子公司&#xff09;开发&#xff0c;并在1980年代末和1990年代…

Armitage的使用

一.简介 Armitage是一个攻击管理工具&#xff0c;它以图形化方式实现了Metasploit框架的自动化攻击。Armitage采用Java构建&#xff0c;拥有跨平台特性。 二.使用 &#xff08;一&#xff09;基本原理 1.入门 1.1开启Armitage 在命令行输入armitage命令会弹出如下对话框&…

黑客工具Armitage

Armitage介绍&#xff1a; Armitage是一款Java写的Metasploit图形界面化的攻击软件&#xff0c;可以用它结合 Metasploit中已知的exploit来针对主机存在的漏洞自动化攻击。通过命令行的方式使用Metasploit难度较高&#xff0c;需要记忆的命令过多&#xff0c;而Armitage完美的…

Armitage的介绍与使用

目录 1.1 Armitage的介绍与原理1.1.1 Armitage主要利用什么漏洞&#xff1f;为什么会出现这种漏洞&#xff1f;攻击怎么实现&#xff1f; 1.2 启动Armitage1.2.1 Armitage启动的三种方法&#xff1a;1.2.2 Armitage启动前初始化&#xff1a; 1.3 使用Armitage生成被控端和主控端…