并发策略-CAS算法

article/2025/8/30 2:55:11

对于并发控制而言,我们平时用的锁(synchronized,Lock)是一种悲观的策略。它总是假设每一次临界区操作会产生冲突,因此,必须对每次操作都小心翼翼。如果多个线程同时访问临界区资源,就宁可牺牲性能让线程进行等待,所以锁会阻塞线程执行。

与之相对的有一种乐观的策略,它会假设对资源的访问是没有冲突的。既然没有冲突也就无需等待了,所有的线程都在不停顿的状态下持续执行。那如果遇到问题了无锁的策略使用一种叫做比较交换(CAS Compare And Swap)来鉴别线程冲突,一旦检测到冲突产生,就重试当前操作直到没有冲突。CAS算法是非阻塞的,它对死锁问题天生免疫,而且它比基于锁的方式拥有更优越的性能。

CAS算法的过程是这样:它包含三个参数 CAS(V,E,N)。V表示要更新的变量,E表示预期的值,N表示新值。仅当V值等于E值时,才会将V的值设置成N,否则什么都不做。最后CAS返回当前V的值。CAS算法需要你额外给出一个期望值,也就是你认为现在变量应该是什么样子,如果变量不是你想象的那样,那说明已经被别人修改过。你就重新读取,再次尝试修改即可。

JDK并发包有一个atomic包,里面实现了一些直接使用CAS操作的线程安全的类型。其中最常用的一个类应该就是AtomicInteger。我们以此为例来研究一下没有锁的情况下如何做到线程安全。

private volatile int value;

这是AtomicInteger类的核心字段,代表当前实际取值,借助volatile保证线程间数据的可见性。

获取内部数据的方法:

public final int get() { return value;
}

我们从源码的实现看看incrementAndGet()的内部实现  

public final int incrementAndGet() {for (;;) {int current = get();int next = current   1;if (compareAndSet(current, next))return next;}
}

代码第二行使用了一个死循环,原因是:CAS的操作未必都是成功的,因此对于不成功的情况,我们就需要进行不断的尝试。第三行取得当前值,接着 1得到新值next。这里我们使用CAS必需的两个参数:期望值以及新值。使用compareAndSet()将新值next写入。成功的条件是在写入的时刻当前的值应该要等于刚刚取到的current。如果不是这样则说明AtomicInteger的值在第3行到第5行之间被其他线程修改过了。当前看到的状态是一个过期的状态,因此返回失败,需要进行下一次重试,直到成功为止。

public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

整体的过程就是这样子,利用CPU的CAS指令,同时借助JNI来完成Java的非阻塞算法。其它原子操作都是利用类似的特性完成的。大概的逻辑应该是这样:

if (this == expect) { this = update return true;
} else { return false;
} 

CAS虽然能高效的解决原子问题,但是CAS也会带来1个经典问题即ABA问题:

因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。

ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A-2B-3A。

从Java1.5开始JDK的atomic包里提供了一个类AtomicStampedReference来解决ABA问题。这个类在内部不仅维护了对象值,还维护了一个时间戳(可以是任意的一个整数来表示状态值)。当设置对象值时,对象值和状态值都必须满足期望值才会写入成功。因此即使对象被反复读写,写会原值,只要状态值发生变化,就能防止不恰当的写入。  

/**  * @param expectedReference 期望值  * @param newReference 写入新值  * @param expectedStamp 期望状态值  * @param newStamp 新状态值  * @return true if successful  */  
public boolean compareAndSet(V   expectedReference,V   newReference, int expectedStamp, int newStamp) {Pair<V> current = pair; return expectedReference == current.reference && expectedStamp == current.stamp &&((newReference == current.reference && newStamp == current.stamp) || casPair(current, Pair.of(newReference, newStamp)));}

> 个人公众号:JAVA日知录 , javadaily.cn

关注公众号获取更多内容


http://chatgpt.dhexx.cn/article/0dPxgCv9.shtml

相关文章

深入理解CAS算法原理

转载自 深入理解CAS算法原理 1、什么是CAS&#xff1f; CAS&#xff1a;Compare and Swap&#xff0c;即比较再交换。 jdk5增加了并发包java.util.concurrent.*,其下面的类使用CAS算法实现了区别于synchronouse同步锁的一种乐观锁。JDK 5之前Java语言是靠synchronized关键字保证…

CAS操作原理

1、什么是CAS&#xff1f; CAS&#xff1a;Compare and Swap&#xff0c;即比较再交换。 jdk5增加了并发包java.util.concurrent.*,其下面的类使用CAS算法实现了区别于synchronouse同步锁的一种乐观锁。JDK 5之前Java语言是靠synchronized关键字保证同步的&#xff0c;这是一…

CAS原理

一、CAS 1.1 CAS概述和作用 CAS的全称是&#xff1a; Compare And Swap(比较相同再交换)。是现代CPU广泛支持的一种对内存中的共享数据进行操作的一种特殊指令。 CAS的作用&#xff1a;CAS可以将比较和交换转换为原子操作&#xff0c;这个原子操作直接由CPU保证。 CAS可以保证…

CAS算法详解

CAS算法 1、CAS概念&#xff1a; CAS是CompareAndSwap的缩写&#xff0c;中文意思是&#xff1a;比较并替换。当要进行CAS操作时&#xff0c;先比较内存地址和原来预期的 地址比较&#xff0c;如果相同&#xff0c;表示这块内存地址没有被修改&#xff0c;可以用新地址替换&…

CAS的原理和使用

CAS 文章目录 CAS一、学习CAS首先了解原子类&#xff1f;1. 何为原子类 二、 CAS是什么1. CAS是什么2. CAS原理3. 使用CAS实例代码4. CAS属于硬件级别保证5. 源码分析 三、CAS底层原理&#xff1f;如果知道&#xff0c;谈谈你对UnSafe的理解1. UnSafe2. 我们知道i线程不安全的&…

对cas算法的理解

cas算法主要关心3个值&#xff1a;内存值V&#xff0c;预期值A&#xff0c;要更新的新值B 如下图所示&#xff1a; 注&#xff1a;t1&#xff0c;t2线程是同时更新同一变量56的值 因为t1和t2线程都同时去访问同一变量56&#xff0c;所以他们会把主内存的值完全拷贝一份到自己…

CAS原理分析

CAS的英文为Compare and Swap 翻译为比较并交换。 CAS加volatile关键字是实现并发包的基石。没有CAS就不会有并发包&#xff0c;synchronized是一种独占锁、悲观锁&#xff0c;java.util.concurrent中借助了CAS指令实现了一种区别于synchronized的一种乐观锁。 什么是乐观锁与…

CAS详解

一、CAS概念 1.1 CAS是什么 Compare And Swap 比较并交换 1. 如果线程的期望值跟物理内存的真实值一样&#xff0c;就更新值到物理内存当中&#xff0c;并返回true 2. 如果线程的期望值跟物理内存的真实值不一样&#xff0c;返回false&#xff0c;那么本次修改失败&#xf…

CAS算法的理解及应用

应用 原子操作类&#xff0c;例如AtomicInteger&#xff0c;AtomicBoolean …适用于并发量较小&#xff0c;多cpu情况下&#xff1b; Java中有许多线程安全类&#xff0c;比如线程安全的集合类。从Java5开始&#xff0c;在java.util.concurrent包下提供了大量支持高效并发访问…

解析CAS算法原理

解析CAS算法原理 什么是CAS&#xff1f;CAS原理概念实现形式底层原理 案例CAS的缺点ABA问题ABA问题如何产生的&#xff1f;原子的引用时间戳原子的引用利用AtomicStampedReference解决ABA问题案例 什么是CAS&#xff1f; CAS&#xff0c;全称Compare And Swap&#xff0c;顾名…

深入解析CAS算法原理

目录 一、CAS的基本概念二、CAS算法理解三、CAS开销四、CAS算法在JDK中的应用 一、CAS的基本概念 CAS&#xff1a;Compare and Swap&#xff0c;即比较再交换&#xff0c;是一种硬件对并发的支持&#xff0c;针对多处理器操作而设计的处理器中的一种特殊指令&#xff0c;用于管…

CAS算法实现

https://blog.csdn.net/bluetjs/article/details/52261490?locationNum15&fps1 1.什么是cas&#xff1f; cas是一种无锁算法&#xff08;非阻塞算法&#xff1a;一个线程的失败或者挂起不应该影响其他线程的失败或者&#xff09;&#xff0c;是compare and swap的缩写&am…

修改Idea的jdk版本

概述 idea很多地方都设置了jdk版本&#xff0c;不同模块的jdk版本也可能不一样&#xff0c;下面整理下涉及jdk或者jre版本的几个地方。 方法一 File - Settings - Build, Execution, Deployment - Build Tools - Maven - Importing 方法二 File - Settings - Build, Exec…

Linux切换jdk版本

开发常识 命令行输入下列命令&#xff0c;然后输入 1 、2、3 选择你想要的 jdk 版本&#xff1a; sudo update-alternatives --config java选择完之后查看 jdk 版本&#xff1a; java -version

如何查看JDK版本信息

如何查看JDK版本号 一、前言二、 Windows的dos窗口 一、前言 在下载某些工具时需要知道自己电脑安装的JDK版本号&#xff0c;这里介绍了一种可以看自己JDK版本号的方法。 二、 Windows的dos窗口 1.电脑WindowsR键&#xff0c;打开命令行窗口   2.在命令行里面输入cmd;   3…

mac安装多个JDK版本

因对不同版本的JDK需求&#xff0c;有时候需要安装多个切换使用&#xff0c;这里我为Mac安装了多个JDK。在已有JDK8的基础上又安装了JDK11。 1、国内镜像下载JDK11&#xff0c;下载地址&#xff1a;https://repo.huaweicloud.com/java/jdk/11.0.29/jdk-11.0.2_osx-x64_bin.dmg…

更改JDK版本

1、更改环境变量&#xff1a;JAVA_HOME的路径&#xff08;此路径下要有bin目录&#xff09; 换成你要用的java的版本所在的路径 2、找到系统变量path下的java路径&#xff0c;将此路径下的三个文件全部删掉 3、打开regedit 修改数据数值即可&#xff0c;如下图切换成功

查看javajdk版本

查看当前电脑的Java/JDK版本的方法 1.winR 打开运行窗口&#xff0c;输入 cmd 2.在控制台中输入java --version或者java -version&#xff0c;即可查看Java版本号 Java所有版本 版本号 发布日期 JDK Version 1.0 1996-01-23 Oak(橡树) JDK Version 1.1 1997-02-19 …

如何更换jdk版本

如何更换jdk版本 因为很多时候需要切换jdk版本。也是走了很多弯路才弄好。此次演示的是将1.7 的版本更换成1.8 的。下面是详细步骤先查看当前版本&#xff0c;输入cmd 打开命令提示符后输入 java -version 即可 可以将1.8 的jdk 于1.7 的jdk 安装在同一个目录下&#xff0c;会…

linux 安装多版本jdk

1、先要安装多个版本的jdk&#xff0c;可以从官网进行下载&#xff0c;然后解压到你需要的目录 例如&#xff1a;/home/xxx/Documents/jdk8 /home/xxx/Documents/jdk17 2、先执行软连接设置&#xff0c;将jdk所在的真实路径建立连接 #数字越大默认级别越高sudo updat…