关于CAS初理解

为自己理一理思路。如果有什么理解不当的地方,希望大家慷慨指正。

CAS详解

先捧一波,上面这篇文章我觉得很好,大家有兴趣可以看看。我中途有些不理解的东西,看完这篇之后,就明白了一些。

那么就开始吧~
因为volatile的不保证原子性,所以才引出了CAS这条大蛇,那么我们先来看看为什么volatile是不保证原子性的。在这里插入图片描述
咱们只看线程一和线程二就行。
简单解释一下这个图,有三个线程访问物理内存,物理内存中有一个初值为0,三个线程都执行的是对其加1 操作。
我们知道线程不能在物理内存中进行操作,需要将数据在自己的工作内存中拷贝一份再进行操作。所以开始时三个线程都在自己的工作内存中存有值0。
当线程1抢占到CPU时,开始进行+1操作,但是我们得知道,在底层真正执行+1操作时是分为三个操作指令来进行操作的。如图:
在这里插入图片描述
当线程1进行到第二个指令结束后,也就是说当线程1获取了当前值,并对它+1,但是还没有将其写入主物理内存时,由于抢占机制,线程1被挂起。
线程2抢到了CPU使用权,这时因为主内存中的共享变量并没有被修改,且线程2工作内存中的值依旧为0,所以当线程2对其进行+1操作。并成功将其写入了主物理内存,这时值变为了1。

当线程1此时再抢上CPU使用权时,并没有在值已经变为1的基础上对其进行操作,而是直接在紧接着挂起前的位置继续进行操作。也就是再次往主物理内存中写入1。这就是写重叠。
这些都是在volatile已经修饰了那个对象之后才进行的。
以上说明了volatile不保证原子性,并且还有此时的线程不安全问题。

那么紧接着,我们是如何解决这种问题的呢。下面是解决方法

AtomicInteger atomicInteger =new AtomicInteger();
    public  void add(){
        atomicInteger.getAndIncrement();
    }

那么为什么atomicInteger.getAndIncrement()能解决volatile不保证原子性的问题呢。这个问题先放一放,咱们后面接着聊。
不是一开始说是CAS吗?怎么说了这么半天也没谈到CAS,别急,接下来就要说到CAS了。
这个CAS到底是个什么呢?
CAScompare and swap的简称。
在这里插入图片描述
那么现在我们说说这个东东的基本思想
假如线程在自己的工作空间对数据进行修改后,准备写入主内存时,线程先判断(比较)主内存现有的值是不是自己修改前的那个值,如果是的话那么就直接将数据写入(交换)主内存,不是的话那么就重新读入 主内存的值在进行操作,直到准备写入的时候主内存的值是自己预期的值,这就是比较并交换的简单思想.
上代码:

public class CASDemo1 {
    public static void main(String[] args){
        AtomicInteger atomicInteger =new AtomicInteger(5);
        System.out.println(atomicInteger.compareAndSet(5,600)+" value ="+atomicInteger.get());
        System.out.println(atomicInteger.compareAndSet(5,988)+" value ="+atomicInteger.get());
    }
}

我们先建一个新对象AtomicInteger atomicInteger =new AtomicInteger(5);
它的初始值为5。也就是我们往主物理内存中写入一个5,他其实就是定义了一个变量。
那么atomicInteger.compareAndSet(5,600)它是干了什么呢?
先解释它里边的参数,参数一是期望值,参数二是更新值。
我们从它的名字就能大致明白,先compare然后set。
compare 比较的是当前线程工作内存修改前的值,与参数一这个期望值是否相同,如果相同,则返回true,并且将更新值写入主物理内存。如果不相同则先返回false,并且不更新主物理内存中的值。
运行结果如下:

true value =600
false value =600

前面我们说到的那个+1操作不保证原子性的解决方式。
但是我们想要更深刻的理解这个东西,还需要去看看它的底层到底是怎么实现的。
我们进去 atomicInteger.getAndIncrement();底层去看,会发现其实是这样实现的,如下图。
他调用的是unsafe 的 getAndAddInt 这个方法,它有三个参数分别是
this 代表那个变量对象。
valueoffset 代表内存偏移量。
I 表示要加的那个值 也就是1.

这里有会有人要问了 那么unsafe是什么嘞? 下图
在这里插入图片描述

在这里插入图片描述
我们来它的最底层,我们可以看到getAndAddInt方法中依旧有三个参数,这和之前说的那三个参数代表的东西是一样的。就不重复了。
直接来看do while里面的东西,在这里我先提醒一下大家,因为这里是原语操作,因此就不存在在这个过程中线程抢占的问题。
首先给var5赋值,赋值为多少呢?
后面这个方法的意思是,根据偏移量找到var1对象的值然后就将var5赋值。
然后在while中判断。
我们看到有一个compareandswapint方法,它有四个参数,分别表示的是:
var1 对象
var2 内存偏移量
var5 刚被赋值
以及将var5 与 外层方法中的第三个参数var4相加
在刚才那个do中,我们得到的其实就是开始时当主存中当前对象的值,我们也可以称它为预期值。然后while中的compareandswapint方法 会再次确定主存中的值是否被改变了,如果改变了,就是false,因为还有一个!取反,即为true,循环继续。

简单的说就是
Unsafe类的getAndAddInt方法体是一个do…while循环,该方法主要是判断当前的对象的预期值(即var5)在主内存中是否一样,通过compareAndSwapInt(var1, var2, var5, var5 + var4)来判断对象的预期值是否一样,如果不一样则一直执行do…while循环,直到预期值和主内存的值一致才结束。

文终~~
如果理解有错,希望大家能指正。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值