CAS详解

第一章 AtomicInteger中涉及到CAS的函数
经过对AtomicInteger源代码的查看,发现用到CAS主要是下面的函数,观察下面的函数,发现主要是用到了这个几个unsafe的函数,unsafe是封装了原子操作的JNI,使用unsafe能把操作原子化,原子操作是指不会被线程调度机制打断的操作,这种操作一旦开始,就一直运行到结束,中间不会有任何线程上下文切换。

unsafe.getAndSetInt

unsafe.compareAndSwapInt

unsafe.getAndAddInt



public final int getAndSet(int newValue) {
        return unsafe.getAndSetInt(this, valueOffset, newValue);
    }
 
 
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
 
    public final boolean weakCompareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
 
    public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }
 
    public final int getAndDecrement() {
        return unsafe.getAndAddInt(this, valueOffset, -1);
    }
 
    public final int getAndAdd(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta);
    }
 
 
    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }
 
    public final int decrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, -1) - 1;
    }
 
    public final int addAndGet(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta) + delta;

第二章 分析unsafe的几个方法
观察getAndSetInt、getAndAddInt的源码,发现它们都调用了compareAndSwapInt函数,那么可以看到传入了四个参数,

第一个参数代表要修改字段所在的对象,如我们其实传入了this,this也就代表AtomicInteger。

第二个参数代表valueOffset代表要修改字段相对于我们的对象在内存中的偏移量,如我们要修改AtomicInteger中的value,传入的就是value的偏移量,字段的偏移量在AtomicInteger的静态代码块(如下)中计算出(感觉这也间接说明静态字段在内存中的位置不会变?)

static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }


第三个参数代表修改前的值,即我们期望的修改前的值。

第四个参数代表修改后的值,即我们期望的修改后的值。

第三章 CAS的缺陷
参考:https://www.cnblogs.com/renqiqiang/articles/10129354.html

3.1 ABA问题
问题描述:线程t1将它的值从A变为B,再从B变为A。同时有线程t2要将值从A变为C。但CAS检查的时候会发现没有改变,但是实质上它已经发生了改变 。可能会造成数据的缺失。

解决方法:CAS还是类似于乐观锁,同数据乐观锁的方式给它加一个版本号或者时间戳,如AtomicStampedReference

3.2 多次自旋导致效率下降
问题描述:多个线程争夺同一个资源时,如果自旋一直不成功,将会一直占用CPU。

解决方法:破坏掉for死循环,当超过一定时间或者一定次数时,return退出。JDK8新增的LongAddr,和ConcurrentHashMap类似的方法。当多个线程竞争时,将粒度变小,将一个变量拆分为多个变量,达到多个线程访问多个资源的效果,最后再调用sum把它合起来。

虽然base和cells都是volatile修饰的,但感觉这个sum操作没有加锁,可能sum的结果不是那么精确。

3.3 多变量共享一致性问题
我们之前都是对一个变量使用循环CAS,如果对多个变量操作,1. 可以加锁来解决。2 .封装成对象类解决。
————————————————
版权声明:本文为优快云博主「逆袭的小学生」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/q610376681/article/details/108905300

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值