当前位置: 首页 > news >正文

Junior Engineer浅谈CAS

系列文章目录

文章目录

  • 系列文章目录
  • 一、乐观锁
    • 1、CAS
    • 2、示例
      • 1、java中的CAS操作是通过sun.misc.unsafe类提供的,这个类提供了底层的内存操作能力
      • 2、手动实现CAS
    • 3、ABA问题以及解决方法
      • 1、什么是ABA问题
      • 2、如何ABA
      • 3、多线程示例,重现ABA问题


一、乐观锁

在这里插入图片描述
在这里插入图片描述

1、CAS

CAS是一种原子操作(不可被中断的操作),用于实现多线程环境下的同步控制,基本逻辑是:

boolean compareAndSwap(int expectedValue, int newValue)

比较当前值是否等于expectedValue
如果相等,则将当前值更新为newValue
返回是否成功,true表示更新成功

CAS的作用
CAS是实现无锁和乐观锁的基础,避免了传统锁带来的性能问题,比如线程阻塞,死锁等

2、示例

1、java中的CAS操作是通过sun.misc.unsafe类提供的,这个类提供了底层的内存操作能力

但是unsafe类是一个不公开的API, 因此java提供了更高层的封装,比如AtomicInteger, AtomicLong等

import java.util.concurrent.atomic.AtomicInteger;public class CASExample {public static void main(String[] args) {AtomicInteger atomicInteger = new AtomicInteger(0);// 尝试将值从 0 改为 1boolean success = atomicInteger.compareAndSet(0, 1);System.out.println("First CAS: " + success); // true// 再次尝试将值从 0 改为 2(此时实际值是 1)success = atomicInteger.compareAndSet(0, 2);System.out.println("Second CAS: " + success); // false// 使用 getAndIncrement(内部使用 CAS)int value = atomicInteger.getAndIncrement();System.out.println("After increment: " + value); // 1}
}

2、手动实现CAS

public class SimpleCAS {private volatile int value;public SimpleCAS(int initialValue) {this.value = initialValue;}public synchronized boolean compareAndSet(int expected, int update) {if (value == expected) {value = update;return true;}return false;}public int getValue() {return value;}public static void main(String[] args) {SimpleCAS cas = new SimpleCAS(0);System.out.println(cas.compareAndSet(0, 1)); // trueSystem.out.println(cas.compareAndSet(0, 2)); // falseSystem.out.println(cas.getValue()); // 1}
}

注意上面compareAndSet是用synchronized 实现的,这仍然是锁机制,不是真正的CAS

3、ABA问题以及解决方法

1、什么是ABA问题

假设变量 x 的值是 A,然后被修改为 B,再被改回 A。

如果你使用普通的 CAS(如 AtomicInteger),它会认为 x 的值没有变,但实际上中间经历了变化。
这可能导致错误的逻辑判断。

2、如何ABA

AtomicStampedReference 通过引入一个版本号(stamp),每次修改时都更新这个版本号,从而可以检测到这种变化

package practice.cas讲解;import java.util.concurrent.atomic.AtomicStampedReference;public class ABACase {public static void main(String[] args) {//初始值为 0,版本号为 0AtomicStampedReference<Integer> ref = new AtomicStampedReference<>(0, 0);
//Java 是按值传递的,如果你传一个 int 变量进去,函数内部修改它不会影响外部的变量。
//因此,我们使用一个 int[] 数组来包装 int,这样可以在方法内部修改数组中的值,外部也能看到。int[] stamp = {0};// 第一次修改//如果期望值和版本号都匹配,则修改成功//如果当前值是0,版本号是0,则将值改为1,版本号改为1boolean success = ref.compareAndSet(0, 1, 0, 1);System.out.println("First CAS: " + success); // true// 第二次修改(ABA 问题)//当前值是1,版本号是1,尝试将值改回0,版本号改为2//此时值从 1 变成 0,但之前曾经是 0。这就是 ABA 问题的体现。success = ref.compareAndSet(1, 0, 1, 2);System.out.println("Second CAS: " + success); // true// 第三次修改(使用正确的 stamp)//当前值是0,版本号是2,尝试将值从0改为3,版本号改为3//什么情况可以返回false?//如果当前值不是0,或者版本号不是2,则修改失败,返回false//因为版本号已经变了,说明中间有其他修改发生过success = ref.compareAndSet(0, 1, 2, 3);System.out.println("Third CAS: " + success); // true}
}

3、多线程示例,重现ABA问题

虽然两个线程都“修改”了值,但最终值还是 0。这说明 AtomicInteger 无法检测到 ABA 问题

import java.util.concurrent.atomic.AtomicInteger;public class ABASample {public static void main(String[] args) {AtomicInteger value = new AtomicInteger(0);Thread t1 = new Thread(() -> {for (int i = 0; i < 100; i++) {int current = value.get();if (current == 0) {// 假设这里有一些操作value.compareAndSet(0, 1);System.out.println("T1: Set to 1");try {Thread.sleep(10); // 模拟延迟} catch (InterruptedException e) {e.printStackTrace();}value.compareAndSet(1, 0);System.out.println("T1: Set back to 0");}}});Thread t2 = new Thread(() -> {for (int i = 0; i < 100; i++) {int current = value.get();if (current == 0) {// 假设这里有一些操作value.compareAndSet(0, 1);System.out.println("T2: Set to 1");try {Thread.sleep(10); // 模拟延迟} catch (InterruptedException e) {e.printStackTrace();}value.compareAndSet(1, 0);System.out.println("T2: Set back to 0");}}});t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Final value: " + value.get());}
}

每次修改都会更新版本号(stamp),即使值回到原点,也能识别出变化。
在这里插入图片描述

import java.util.concurrent.atomic.AtomicStampedReference;public class ABASolution {public static void main(String[] args) {AtomicStampedReference<Integer> ref = new AtomicStampedReference<>(0, 0);Thread t1 = new Thread(() -> {for (int i = 0; i < 100; i++) {int[] stamp = {0};Integer current = ref.get(stamp);if (current == 0) {boolean success = ref.compareAndSet(0, 1, 0, 1);if (success) {System.out.println("T1: Set to 1 with stamp 1");try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}success = ref.compareAndSet(1, 0, 1, 2);if (success) {System.out.println("T1: Set back to 0 with stamp 2");}}}}});Thread t2 = new Thread(() -> {for (int i = 0; i < 100; i++) {int[] stamp = {0};Integer current = ref.get(stamp);if (current == 0) {boolean success = ref.compareAndSet(0, 1, 0, 1);if (success) {System.out.println("T2: Set to 1 with stamp 1");try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}success = ref.compareAndSet(1, 0, 1, 2);if (success) {System.out.println("T2: Set back to 0 with stamp 2");}}}}});t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}int[] finalStamp = {0};Integer finalValue = ref.get(finalStamp);System.out.println("Final value: " + finalValue + ", stamp: " + finalStamp[0]);}
}
http://www.xdnf.cn/news/1406521.html

相关文章:

  • 【百度】C++开发(25届提前批 一面)面经
  • 时序数据库
  • GitHub 热榜项目 - 日榜(2025-08-31)
  • 使用cursor claude sonnet4的一些感受
  • PY32F002不小心设置了SWD复用的恢复
  • Chrome++插件与GreenChrome:增强Chrome浏览器功能
  • Spring Boot 3.0 应用 HTTP 到 HTTPS 技术改造方案
  • 《潮汐调和分析原理和应用》之四S_Tide使用2
  • Java中不太常见的语法-总结
  • 架构进阶——解读 69页 方法轮IT规划培训 架构-重点-细节【附全文阅读】
  • Shell编程核心入门:参数传递、运算符与流程控制全解析
  • 2025年9月计算机二级C++语言程序设计——选择题打卡Day11
  • 学习日志41 python
  • Linux/UNIX系统编程手册笔记:文件I/O、进程和内存分配
  • vue2下拉菜单
  • 【小宁学习日记5 PCB】电路定理
  • 9. 函数和匿名函数(一)
  • 快消品牌如何用 DAM 管理万张素材?
  • 【光照】[光照模型]是什么?以UnityURP为例
  • C++的反向迭代器
  • BEV-VAE
  • 二进制方式安装部署 Logstash
  • Java试题-选择题(23)
  • 【Linux基础】深入理解计算机启动原理:MBR主引导记录详解
  • 并发编程:Java中的多线程与线程池!
  • 魔方的使用
  • LangGraph 深度解析(二):掌握 LangGraph 函数式 API 的状态化 AI 工作流
  • 每日算法题【二叉树】:堆的实现、堆排序的实现、文件中找TopK
  • [光学原理与应用-338]:ZEMAX - Documents\Zemax\Samples
  • 吴恩达机器学习作业九:kmeans聚类