CAS算法实现

https://blog.csdn.net/bluetjs/article/details/52261490?locationNum=15&fps=1

1.什么是cas?

    cas是一种无锁算法(非阻塞算法:一个线程的失败或者挂起不应该影响其他线程的失败或者),是compare and swap的缩写,表示为比较并交换的意思

2.cas算法:

    cas有三个操作数,v内存值,旧的预算值A,需要修改的值B,当且预期值A和内存值V相等时,将内存值修改为B,否则什么都不做、

3.++i操作

    

public final int incrementAndGet() {

    for (;;) {

        int current = get();

        int next = current + 1;

        if (compareAndSet(current, next))

            return next;

    }

}

这里采用了CAS操作,每次从中读取数据都会将此数据和+1后的结果进行CAS操作,如果成功则返回结果否则重试到成功为止,而这里的compareAndSet利用JNT( JNI:Java Native Interface为JAVA本地调用,允许java调用其他语言。完成CPU的指令操作

compareAndSet的代码

public final boolean compareAndSet(int expect, int update) {   

    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);

    }

compareAndSwapInt类似这样的逻辑:

if(this==except){

    this=update

    return true;

}else{

    return false;

}

4.CAS原理:

    CAS是通过调用JNT代码实现的,而compareAndSwapInt就是就是借用CAS来调用CPU底层指令实现的下面通过(inel x86)来解释CAS的实现原理

public final boolean compareAndSet(int expect, int update) {   

    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);

    }

可以看到这是个本地方法调用。这个本地方法在openjdk中依次调用的c++代码为:unsafe.cpp,atomic.cpp和atomicwindowsx86.inline.hpp。这个本地方法的最终实现在openjdk的如下位置:openjdk-7-fcs-src-b147-27jun2011\openjdk\hotspot\src\oscpu\windowsx86\vm\ atomicwindowsx86.inline.hpp(对应于windows操作系统,X86处理器)。下面是对应于intel x86处理器的源代码的片段:

 

// Adding a lock prefix to an instruction on MP machine

// VC++ doesn't like the lock prefix to be on a single line

// so we can't insert a label after the lock prefix.

// By emitting a lock prefix, we can define a label after it.

#define LOCK_IF_MP(mp) __asm cmp mp, 0 \

__asm je L0 \

__asm _emit 0xF0 \

__asm L0:

 

 

inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) {

// alternative for InterlockedCompareExchange

int mp = os::is_MP();

__asm {

mov edx, dest

mov ecx, exchange_value

mov eax, compare_value

LOCK_IF_MP(mp)

cmpxchg dword ptr [edx], ecx

}

}

 

如上面源代码所示,程序会根据当前处理器的类型来决定是否为cmpxchg指令添加lock前缀。如果程序是在多处理器上运行,就为cmpxchg指令加上lock前缀(lock cmpxchg)。反之,如果程序是在单处理器上运行,就省略lock前缀(单处理器自身会维护单处理器内的顺序一致性,不需要lock前缀提供的内存屏障效果)。

 

5.CAS的缺点:

    CAS虽然能高效的保证原子操作,但CAS还是存在三大问题,ABA问题,循环时间长开销大,只能保证一个共享变量的原子操作

    1.ABA问题因为CAS需要在操作时检查值有没有发生变化,,如果没有发生变化就更新,但是如果一个值原来是A变成了B,再变成A,那么CAS检查不到他的值发生变化,而实际上是变化了的,A->B->A,也就是说,CAS不能发现数值是否改变的过程,他只会看到结果,

    问题实例化:假如银行扣费,有两个线程操作,正确应该是一个扣费成功,一个失败,而我应少50元,假如第一个线程扣费50元后,第二个线程检查之前,我收到转账50元,那么线程2还会对我进行扣费,所以我被扣100元

    解决方法:添加版本号,1A->2B->3A

    2.循环时间长:开销大:如果CAS长时间不成功,则会带来非常大的开销,如果,jvm支持pause指令,效率会有一定的提升,pause指令有两个作用,1、延迟流水线执行指令,使cpu不会消耗过多执行资源2、避免退出循环的时候因内存顺序冲突而引起cpu流水线被清空,从提高CPU的执行效率

    3.只能保证一个共享变量的原子操作:当对一个共享变量执行操作时,我们可以使用循环的方式来保证操作的原子性,这时候就要用锁

6.CAS的优化

    java8对CAS的优化:为了防止CAS不断自旋,消耗大量资源,于是java8推出了一个LongAdder,他使用分段CAS自动分段迁移的方式来大幅度提升多线程高并发执行CAS操作0性

https://mp.weixin.qq.com/s?src=11×tamp=1563419615&ver=1735&signature=R8p-4Q7AnqkeEaViijsaymppXy-O34bCEQO9GHt-Tw*V3me0lVHkfN92reDkOks-94na9RUIuiKqarUUJhdK6SjkjuU1HgGDT34ots1-T7Fs*xO1ywUWW6jOjokZt-eK&new=1

        在LongAdder的底层实现中,首先有一个base值,刚开始多线程不停地累加数值,都是对base进行累加的,比如刚开始累加成base=5;接着,如果发现并发更新的线程数过多,就开始实行分CAS机制也就是内部会创建一个Cell数组,没个数组是一个分段值,它内部也实现了自动分段迁移的机制,就是如果一个Cell的value执行CAS失败了,那么他就会自动寻找另外一个Cell分段内的value值进操作

     优点是把CAS的计算压力分散到不同的Cell中了,解决了线程空自旋、自旋不等待CAS操作的问题,让一个CAS来执行时可能尽快的完成这个操作

 

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值