浅谈CAS

前言:前文讨论了volatile保证工作内存和主内存之间的数据一致性问题(可见性),但是运算的原子性没有保证,那么使用CAS就可以用来解决这个原子性运算问题。

  CAS,即Compare And Swap,是一种典型乐观锁的实现。来自大佬的定义:CAS需要有3个操作数:内存地址V,旧的预期值A,即将要更新的目标值B。CAS指令执行时,当且仅当内存地址V的值与预期值A相等时,将内存地址V的值修改为B,否则就什么都不做。整个比较并替换的操作是一个原子操作。Java中有自带的原子类数据类型,例如AtomicInteger,调用unsafe包实现  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class CASDemo {  
 
      public static AtomicInteger i = new AtomicInteger(0);
 
    private static final int THREADS_COUNT = 20;
 
    private static void inCrease() {
        i.incrementAndGet();
    }
 
    public static void main(String[] args) throws InterruptedException {
        Thread[] threads = new Thread[THREADS_COUNT];
        CountDownLatch countDownLatch = new CountDownLatch(THREADS_COUNT);
        for (int j = 0; j < THREADS_COUNT; j++) {
            threads[j] = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int k = 0; k < 10000; k++) {
                        inCrease();
                    }
                    countDownLatch.countDown();
                }
            });
            threads[j].start();
        }
        countDownLatch.await();
        System.out.println(i);
    }
}

此时输出结果为200000,为线程安全。

  unsafe的cas操作在实现上也是在汇编层面lock加锁,所以同时具备volatile的性质:可见性和禁止指令重排序。

  CAS也有它的问题,一个是循环开销问题,CAS经常和循环一起使用,执行失败就会重试,如果一个变量的并发操作量很大的时候,这时循环执行就有大量失败和重试次数,造成过大的Cpu开销。

  二是ABA问题,CAS只是关心Value和ExpectedValue是否相等,相等则继续执行,那么如果执行期间这个变量被其他线程使用过,但是结果不变,这时就CAS就会误认为该值没有变化过。Java引入了“AtomicStampedReference”,通过同时比较Value的值和版本控制解决了这个问题,MySQL的MVCC(多版本并发控制)也是采取了类似的设计思想。

posted @   小皮睡不醒  阅读(84)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~
点击右上角即可分享
微信分享提示