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

CAS 有什么问题?如何解决这些问题?

面试资料大全|各种技术资料-2000G

一、ABA 问题:最隐蔽的数据一致性问题

问题本质

线程1 线程2 内存 读取值 A 读取值 A 修改 A→B 修改 B→A CAS(A→C) 成功! 线程1 线程2 内存
  • 危险场景:链表头指针变更
    初始: Head -> NodeX
    线程1: 准备CAS(Head, NodeX, NodeY)
    线程2: 删除NodeX → 添加NodeZ → 重新添加NodeX
    结果: Head指向已被删除的NodeX
    

解决方案

1. 版本号机制(推荐)
// Java 的 AtomicStampedReference
AtomicStampedReference<String> ref = new AtomicStampedReference<>("A", 0); // 初始值+版本号int[] stampHolder = new int[1];
String current = ref.get(stampHolder);
int currentStamp = stampHolder[0];// 更新时检查值和版本号
boolean success = ref.compareAndSet(current, "C", currentStamp, currentStamp + 1
);
2. 时间戳机制
struct TaggedPointer {void* ptr;uint64_t timestamp; // 纳秒时间戳
};bool tagged_CAS(TaggedPointer* dest, TaggedPointer expected, TaggedPointer new_val) {if (dest->ptr == expected.ptr && dest->timestamp == expected.timestamp) {*dest = new_val;return true;}return false;
}
3. 垃圾回收语言的特殊方案
// 利用GC特性避免ABA(仅限托管环境)
AtomicReference<Object> ref = new AtomicReference<>();// 每次修改创建新对象
Object current = ref.get();
Object newObj = new Object(); // 旧对象会被GC回收
ref.compareAndSet(current, newObj);

二、循环开销问题:高竞争下的性能灾难

问题表现

CAS操作
成功?
结束
自旋重试
  • 性能影响
    • CPU 使用率飙升至 100%
    • 实际吞吐量急剧下降
    • 影响同核心其他线程

解决方案

1. 指数退避策略
int failures = 0;
while (!casUpdate()) {failures++;long delay = (long) Math.pow(2, Math.min(failures, 10)); // 2^failuresLockSupport.parkNanos(delay * 1000); // 纳秒级休眠
}
2. 自适应自旋(JVM 内部优化)
// HotSpot JVM 实现伪代码
int spins = 0;
while (!tryLock()) {if (spins < MAX_SPINS) {spins++;Thread.onSpinWait(); // JDK9+ 提示CPU优化} else {park(); // 挂起线程}
}
3. LongAdder 分段计数(Java 8+)
// 高竞争计数器替代方案
LongAdder counter = new LongAdder();
counter.increment(); // 内部使用Cell[]分散竞争// 实现原理
┌───────────┐       ┌─────┐
│ BaseValue ├───┬──►│Cell0│
└───────────┘   │   └─────┘├──►│Cell1││   └─────┘└──►│Cell2│└─────┘

三、单变量限制:复合操作的原子性缺失

问题场景

// 需要原子更新两个变量
class Account {int balance;int version;
}// 错误尝试:
AtomicReference<Account> ref = ...;
Account current = ref.get();
Account newAcc = new Account(current.balance+100, current.version+1);
ref.compareAndSet(current, newAcc); // 非原子创建对象!

解决方案

1. 对象打包法
// 创建不可变对象
class State {final int x;final int y;State(int x, int y) { ... }
}AtomicReference<State> ref = new AtomicReference<>(new State(0, 0));State current = ref.get();
State newState = new State(current.x+1, current.y-1);
ref.compareAndSet(current, newState);
2. 锁+CAS混合模式
// 对复合操作加锁
Lock lock = new ReentrantLock();
AtomicInteger x = new AtomicInteger(0);
AtomicInteger y = new AtomicInteger(0);void updateBoth() {lock.lock();try {int curX = x.get();int curY = y.get();// 复合操作x.compareAndSet(curX, curX+1);y.compareAndSet(curY, curY-1);} finally {lock.unlock();}
}
3. 事务内存(实验性)
// Java 的 LVar (Laboratory Virtual Machine)
@AtomicMethod
public void transfer(Account from, Account to, int amount) {from.balance -= amount;to.balance += amount;
}

四、额外问题:内存顺序与可见性

问题表现

Store
Load
缓存一致性协议
CPU1
Cache
CPU2
Memory
  • 危险场景
    // 线程1
    data = 42;               // 普通写
    ready.compareAndSet(false, true); // CAS写// 线程2
    if (ready.get()) {        // CAS读System.out.println(data); // 可能看到旧值!
    }
    

解决方案:内存屏障

// Java volatile/CAS 自带内存屏障
AtomicInteger ready = new AtomicInteger();// 线程1
data = 42;                                 // StoreStore屏障
ready.compareAndSet(0, 1); // 包含StoreLoad屏障// 线程2
if (ready.get() == 1) {      // 包含LoadLoad+LoadStore屏障System.out.println(data); // 保证看到data=42
}

五、综合解决方案对比

问题类型解决方案适用场景性能影响实现复杂度
ABA问题版本号(AtomicStampedReference)指针/引用类型更新
时间戳系统级编程
GC依赖方案托管环境(JVM/.NET)
循环开销指数退避中等竞争场景可变
LongAdder分段高竞争计数器极低
自适应自旋JVM内置优化无需实现
单变量限制对象打包<5个字段的复合状态
锁+CAS混合复杂事务操作
事务内存实验性/研究场景极高极高

六、最佳实践指南

1. 选型决策树

单变量
多变量
需要原子操作?
操作复杂度
基础Atomic类
竞争程度
对象打包法
锁+CAS混合
普通变量
是否ABA敏感
带版本号Atomic
普通Atomic

2. 性能优化技巧

// 1. 缓存行填充防止伪共享
@Contended // Java 8+ 注解
public class PaddedAtomicLong extends AtomicLong {public volatile long p1, p2, p3, p4, p5, p6 = 7L;
}// 2. 批量操作减少CAS次数
class BatchedUpdater {private final AtomicLong counter = new AtomicLong();private ThreadLocal<Long> buffer = ThreadLocal.withInitial(() -> 0L);public void increment() {buffer.set(buffer.get() + 1);if (buffer.get() > 1000) { // 批量提交counter.addAndGet(buffer.get());buffer.set(0L);}}
}// 3. 热点分离
Map<Long, AtomicLong> shardedCounters = new ConcurrentHashMap<>();
void increment(long userId) {AtomicLong counter = shardedCounters.computeIfAbsent(userId % 16, k -> new AtomicLong());counter.incrementAndGet();
}

3. 架构级解决方案

请求
路由
路由
路由
Client
Gateway
分片1
分片2
分片3
本地计数器
LocalCounter2
LocalCounter3
全局聚合器

面试资料大全|各种技术资料-2000G

在这里插入图片描述

相关文章:

  • 聊城网站建设信息关键词站长工具
  • 高端品牌网站建设(杭州)怎么创建自己的网址
  • 江苏网站建设包括哪些哈尔滨网站建设
  • 品牌网站建设 优帮云色目人
  • 工信部备案查询南京seo公司哪家
  • 网站后台登陆不了网站建设的基本
  • 《解锁Web游戏潜力:手柄操控的进阶法则》
  • 【nRF52832】【环境搭建 1】【ubuntu下搭建nRF52832开发环境】
  • 2 Studying《BPF.Performance.Tools》1-9
  • AnythingLLM+Ollama搭建本地知识库
  • 【Ansible】Ansible介绍
  • Java 的强制类型转换
  • 基于STM32的个人健康助手的设计
  • 参数两和显存占用计算
  • 高性能的内存数据存储系统-Redis
  • 微信小程序适配 iPhone 底部导航区域(safe area)的完整指南
  • C# 中 逻辑运算符 - 逻辑与,短路
  • C# Avalonia 绑定模式 Mode 的区别,它们的应用场景
  • thinkphp8之文件上传
  • 永磁同步电机无速度算法--基于同步旋转坐标系锁相环的滑模观测器
  • Matlab里的默认配色推荐
  • 时序数据库 TDengine 助力华锐 D5 平台实现“三连降”:查询快了,机器少了,成本也低了
  • Node.js-fs模块
  • FPGA笔记——ZYNQ-7020运行PS端的USB 2.0端口作为硬盘
  • 多模态AI:让机器像人一样“全感官”理解世界
  • Vue计算属性与监视属性