CAS原理

1、什么是CAS?java

比较并交换(compare and swap),它是一条CPU并发原语。判断内存某个位置的值是否为预期值,若是是的话则更新为新值,这个过程是原子的。多线程

原语是操做系统的用语,是由若干条指定组成,用于完成某个功能的一个过程,而且原语的执行必须是连续的,在执行过程当中不容许被中断,也就是说CAS是一条CPU的原子指令,不会形成所谓的数据不一致问题。并发

public class Solution
{
    public static void main(String[] args)
    {
        AtomicInteger atomicInteger = new AtomicInteger(5);
        System.out.println(atomicInteger.compareAndSet(5,22)+ ". current data is: " + atomicInteger.get());
        System.out.println(atomicInteger.compareAndSet(5,33)+ ". current data is: " + atomicInteger.get());
    }
}

运行结果:this

true. current data is: 22
false. current data is: 22
public class Solution
{
    public static void main(String[] args)
    {
        AtomicInteger atomicInteger = new AtomicInteger(5);
        System.out.println(atomicInteger.compareAndSet(5,22)+ ". current data is: " + atomicInteger.get());
        System.out.println(atomicInteger.compareAndSet(22,33)+ ". current data is: " + atomicInteger.get());
    }
}

运行结果:atom

true. current data is: 22
true. current data is: 33

结果说明:spa

一、第一次运行,因为在物理内存中的值是5,因此符合expected的值,就修改成了22,而第二条语句的expected为5,可是如今值已经为22,因此不符合预期,就不能改变值。操作系统

二、第二次运行,每次的expected的值都为物理内存中的值,因此都进行了修改。线程

2、CAS底层原理,什么是UnSafe?指针

要点:UnSafe类(存在rt.jar中)+CAS自旋锁code

AtomicInteger的源码

public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;
 
    // setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;
 
    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }
 
    private volatile int value;
}

一、Unsafe类

这是CAS的核心类,因为Java方法没法直接访问底层系统,须要经过本地(native)方法来访问,UnSafe至关于一个后门,基于该类能够直接操做特定内存的数据

UnSafe类存在于sun.misc包中,其内部方法操做能够像C的指针同样直接操做内存,所以Java中CAS操做执行依赖于UnSafe类

UnSafe类中的全部方法都是native修饰的,也就是说UnSafe类中的方法都直接调用操做系统底层资源执行相应任务

二、变量valueOffset,表示该变量在内存中的偏移地址,由于UnSafe就是根据内存偏移地址获取数据的。

//this:当前对象
//valueOffset:内存偏移量
 public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
 }
//var1 AtomicInteger对象自己
//var2 该对象值的引用地址
//var4 须要变更的值
//var5 用var1 var2找出主内存中真实的值
//用该对象当前值与var5比较,若是相同,更新var5+var4返回true,若是不一样,继续取值比较,直到更新完成
public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    do {
        var5 = this.getIntVolatile(var1, var2);
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
 
    return var5;
}  

总结:getAndIncrement()底层调用unsafe类方法,传入三个参数,unsafe.getAndAddInt()底层使用CAS思想,若是比较成功加1,若是比较失败则从新得到,再比较,直到成功

三、变量value用volatile修饰,保证了多线程之间的内存可见性

2、CAS缺点?

一、循环时间开销大(若是CAS失败,会一直尝试)

2、只能保证一个共享变量的原子操做。(对多个共享变量操做时,循环CAS没法保证操做的原子性,只能用加锁来保证)

三、存在ABA问题

4、原子类AtomicInteger类ABA问题及解决方案

一、ABA问题是怎么产生的?

当第一个线程执行CAS(V,E,U)操做,在获取到当前变量V,准备修改成新的值U以前。另外两个线程已经连续修改了两次变量V的值,使得该值又恢复为旧的值,这样咱们就没法正确判断这个变量是否已经被修改过

二、ABA问题的解决方案:

AtomicStampedReference:是一个带有时间戳的对象引用,在每次修改后,不只会设置新的值,还会记录修改的时间

AtomicMarkableReference:维护的是一个Boolean值的标识,这种方式并不能彻底防止ABA问题的发生,只能减小ABA发生的几率

public class Solution
{
    static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1);

    public static void main(String[] args) throws InterruptedException
    {
        System.out.println("========ABA问题的产生=========");
        new Thread(() ->
        {
            atomicReference.compareAndSet(100, 101);
            atomicReference.compareAndSet(101, 100);
        }, "t1").start();

        new Thread(() ->
        {
            try
            {
                TimeUnit.SECONDS.sleep(1);
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
            System.out.println(atomicReference.compareAndSet(100, 2019) + " " + atomicReference.get());
        }, "t2").start();
        TimeUnit.SECONDS.sleep(2);

        System.out.println("========ABA问题的解决=========");

        new Thread(() ->
        {
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() + "线程第1次版本号:" + stamp);
            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() + "线程第2次版本号:" + atomicStampedReference.getStamp());
            atomicStampedReference.compareAndSet(101, 100, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
            System.out.println(Thread.currentThread().getName() + "线程第3次版本号:" + atomicStampedReference.getStamp());

        }, "t3").start();

        new Thread(() ->
        {
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() + "线程第1次版本号:" + stamp);
            try
            {
                TimeUnit.SECONDS.sleep(3);
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
            boolean result = atomicStampedReference.compareAndSet(100, 2019, stamp, stamp + 1);
            System.out.println(Thread.currentThread().getName() + "修改为功否:" + result + "  当前最新版本号:" + atomicStampedReference.getStamp());
            System.out.println(Thread.currentThread().getName() + "当前最新值:" + atomicStampedReference.getReference());

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

运行结果:

========ABA问题的产生=========
true 2019
========ABA问题的解决=========
t3线程第1次版本号:1
t4线程第1次版本号:1
t3线程第2次版本号:2
t3线程第3次版本号:3
t4修改为功否:false  当前最新版本号:3
t4当前最新值:100

5、原子更新引用

class User
{
    User(String name, int age)
    {
        this.name = name;
        this.age = age;
    }
    private String name;
    private int age;
}

public class Solution
{
    static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1);

    public static void main(String[] args) throws InterruptedException
    {
        AtomicReference<User> atomicReference = new AtomicReference<>();

        User user = new User("monster", 18);
        User updateUser = new User("jack", 25);

        atomicReference.set(user);
        System.out.println(atomicReference.get());
        atomicReference.compareAndSet(user, updateUser);
        System.out.println(atomicReference.get());
    }
}

运行结果:

User@74a14482
User@1540e19d
相关文章
相关标签/搜索