CAS的底层原理

1,什么是CAS?

有锁并发与无锁并发

有锁并发的典型代表synchronized关键字,其原理是通过当前线程持有当前对象锁,从而拥有访问权限,而其他没有持有当前对象锁的线程无法拥有访问权限,也就保证了线程安全。

无锁并发,即不加锁也能保证并发执行的安全性,其核心原理就是CAS。

乐观锁与悲观锁

悲观锁总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。

乐观锁总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据。

乐观锁适用于冲突较少的情况,悲观锁适用于冲突较多的情况。

CAS的原理

CAS的全称是Compare And Swap 即比较交换,其算法核心思想是执行函数CAS(V,E,N),V表示要更新的变量,E表示预期值,N表示新值。

如果V值等于E值,则将V的值设为N。若V值和E值不同,则说明已经有其他线程做了更新,则当前线程不执行更新操作,可以选择重新读取该变量尝试再次修改该变量,也可以放弃操作。

CAS操作即使没有锁,同样知道其他线程对共享资源操作影响,由于无锁操作中没有锁的存在,因此不可能出现死锁的情况。

Java中CAS操作的执行依赖于Unsafe类的方法,Unsafe类存在于sun.misc包中,其内部方法操作可以像C的指针一样直接操作内存,是非安全的,因此Java官方也不建议直接使用的Unsafe类。

原子类Atomic

从JDK 1.5开始提供了java.util.concurrent.atomic包,在该包中提供了许多基于CAS实现的原子操作类,常见的原子类如AtomicInteger,可以实现一个int数据的增加和减少操作,

一般如 i++或者 i=i+1 这些操作时,在多线程情况下是存在并发安全问题的,因为这个操作在cpu执行时并不是一条指令,

i++,其实是先读i的值,再把i的值加1,然后把i写入内存。

以前如果我们要保证这些操作的并发安全必须对代码加上锁(synchronized关键字),而使用AtomicInteger,调用它的增加和减少方法,可以避免线程安全问题,它的底层就是通过CAS实现。

ABA问题及其解决方案

CAS操作会引起ABA问题:

假如线程1准备用CAS将变量的值由A替换为C,在此之前,线程2将变量的值由A替换为B,又由B替换为A,然后线程1执行CAS时发现变量的值仍然为A,所以CAS成功。但实际上这时的现场已经和最初不同了,尽管CAS成功,但可能存在潜藏的问题。

现有一个用单向链表实现的堆栈,栈顶为A,这时线程T1已经知道A.next为B,然后希望用CAS将栈顶替换为B;

在T1执行上面这条指令之前,线程T2介入,将A、B出栈,再pushD、C、A;

此时轮到线程T1执行CAS操作,检测发现栈顶仍为A,所以CAS成功,栈顶变为B,但实际上B.next为null,所以此时的情况变为:堆栈中只有B一个元素,C和D组成的链表不再存在于堆栈中,C、D丢掉了。

Java并发包为了解决这个问题,提供了一个带有标记的原子引用类“AtomicStampedReference”,它可以通过控制变量值的版本来保证CAS的正确性。每次在执行数据的修改操作时,都会带上一个版本号,一旦版本号和数据的版本号一致就可以执行修改操作并对版本号执行+1操作,否则就执行失败。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值