其他分享
首页 > 其他分享> > CAS

CAS

作者:互联网

CAS

cas是什么

Compare-And-Swap 比较并交换,是一条CPU的并发原语

判断内存中某个位置的值是否为预期值,如果是则更改为新的值,这个过程是原子的

如果主内存中的值和期望中的值一样,就修改为现在想要的值,如果主内存中的值和期望中的值不一样,就不修改

package com.audition.cas;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created with IntelliJ IDEA.
 *
 * @Auther: 两杯水
 * @Date: 2021/03/01/10:11
 * @Description:
 * cas是什么?比较并交换
 *
 */
public class CasDemo1 {
    public static void main(String[] args) {
        //得到的默认值是5
        AtomicInteger atomicInteger = new AtomicInteger(5);
        //compareAndSet:比较内存中的值是否是5,如果是就修改为2021
        System.out.println(atomicInteger.compareAndSet(5, 2021)+ "\t current data:" +atomicInteger.get());
        System.out.println(atomicInteger.compareAndSet(5, 1234)+ "\t current data:" +atomicInteger.get());
    }
}

cas的底层原理

比较当前工作内存中的值和主内存中的值,如果相同则执行规定操作,否则继续比较,直到主内存中的值和工作内存中的值一致为止

自旋锁和UnSafe类

在这里插入图片描述

UnSafe类:CAS核心类,Java无法直接访问底层系统,其内部方法可以向C指针一样操作内存,java CAS操作依靠于UnSafe类,UnSafe根据内存偏移地址获取数据的,

所以说cas是一种系统原语,原语的执行必须是连续的,在执行过程中不能被终端,CAS是cpu的原子指令,所以不会造成数据不一致问题

在这里插入图片描述

CAS应用:

CAS有3个操作数,内存值V,旧的预期值A,要修改的更新值B

当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做

CAS缺点:

循环时间长:如果CAS失败,会一直进行尝试,会给CPU造成很大开销

只能保证一个共享变量的原子操作

引来ABA问题

ABA问题:

两个线程同时取主物理内存中的同一内存中的数据,但是由于其中一个线程比另一个线程快,中间修改了内存数据多次,在另一个线程访问之前,又把内存数据修改为第一次获取值,。当另一个线程回去访问时,并不知道数据已经修改了多次了,只看到数据一样

解决ABA问题

代码示例:

package com.audition.cas;

import lombok.*;

import java.util.concurrent.atomic.AtomicReference;

/**
 * Created with IntelliJ IDEA.
 *
 * @Auther: 两杯水
 * @Date: 2021/03/01/11:24
 * @Description:原子引用  AtomicReference
 */
@Getter
@Setter
@AllArgsConstructor
@ToString
class User{
    String userName;
    int age;
}
public class CasDemo2 {
    public static void main(String[] args) {
        User z3 = new User("z3",23);
        User li4 = new User("li4",25);
        AtomicReference<User> reference = new AtomicReference<>();
        reference.set(z3);
        System.out.println(reference.compareAndSet(z3, li4)+"\t" +reference.get().toString());
        System.out.println(reference.compareAndSet(z3, li4)+"\t" +reference.get().toString());


    }

}

新增一种机制,修改版本号(类似时间戳)

package com.audition.cas;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * Created with IntelliJ IDEA.
 *
 * @Auther: 两杯水
 * @Date: 2021/03/01/11:34
 * @Description:  AtomicStampedReference
 */
public class ABADemo {
     static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
     static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100,1);
    public static void main(String[] args) {
        System.out.println("=========以下是ABA问题的产生==============");
        new Thread(()->{
            atomicReference.compareAndSet(100,101);
            atomicReference.compareAndSet(101,100);
        },"t1").start();

        new Thread(()->{
            //暂停一秒钟,保证t1线程完成了一次ABA操作
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(atomicReference.compareAndSet(100,200)+"\t"+atomicReference.get());
        },"t2").start();
        //暂停一会线程
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("=========以下是ABA问题的解决==============");
        new Thread(()->{
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName()+"\t第一次版本号"+stamp);
            //暂停1秒钟T3线程
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            atomicStampedReference.compareAndSet(100,101,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
            System.out.println(Thread.currentThread().getName()+"\t第二次版本号"+stamp);
            atomicStampedReference.compareAndSet(101,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
            System.out.println(Thread.currentThread().getName()+"\t第三次版本号"+stamp);
        },"t3").start();

        new Thread(()->{
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName()+"\t第一次版本号"+stamp);
            //暂停3秒钟T4线程,确保t3完成了一次ABA操作
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            boolean andSet = atomicStampedReference.compareAndSet(100, 2019, stamp, stamp + 1);
            System.out.println(Thread.currentThread().getName()+"\t修改成功否:"+andSet + "\t当前实际版本号"+atomicStampedReference.getStamp());
            System.out.println(Thread.currentThread().getName()+"\t当前实际最新值: "+ atomicStampedReference.getReference());

        },"t4").start();
    }
}

标签:compareAndSet,CAS,System,内存,println,atomicStampedReference,out
来源: https://blog.csdn.net/weixin_44413260/article/details/114281715