CAS机制

作者: 奔跑吧李博 | 来源:发表于2021-04-06 01:53 被阅读0次
原子操作

原子操作(atomic)就是不可分割的操作,在计算机中,就是指不会因为线程调度被打断的操作。通俗讲就是你要么全部做完,要么一点也不做。

举例:

启动两个线程,每个线程中让静态变量count循环累加100次。那么其结果很可能会出现错误。
而给累加操作加上sychronized代码块,就能够得到正确的结果。这就叫实现了原子性操作,从而实现了线程安全。

那么这样做我们就安心了吗?有没有发现,sychronized用在这里反复加锁释放锁消耗性能,为了这个count++的操作有点大炮打蚊子了。这里用sychronized来实现原子性并不是高效的,那么就可以使用原子操作类来轻量级解决该问题。

原子操作类:

指的是java.util.concurrent.atomic包下,一系列以Atomic开头的包装类。例如AtomicBoolean,AtomicInteger,AtomicLong。它们分别用于Boolean,Integer,Long类型的原子性操作。

将count用AtomicInteger来声明,表明是原子操作类。最终的输出结果同样可以保证是200。Atomic类实现的底层正式使用了CAS机制。

CAS(Compare And Swap) 比较并替换

利用现代处理器都支持CAS的指令,循环这个指令,直到成功为止。

从思想上来说,Synchronized属于悲观锁,悲观地认为程序中的并发情况严重,总有别的线程要来抢占锁,所以每个线程都会先下手为强,先抢占到锁去执行操作。

而CAS属于乐观锁,乐观地认为程序中的并发情况不那么严重,先让自己执行操作,再去试图修改资源。

CAS机制当中使用了3个基本操作数:内存地址V,旧的值A,要修改的新值B。

更新一个变量的时候,只有当变量的值A和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改为B。

CAS过程演示图

1.在内存地址V当中,存储着值为1的变量。
2.此时线程1想要把变量的值增加1。对线程1来说,旧的预期值A=1,要修改的新值B=2。
3.在线程1要提交更新之前,另一个线程2抢先一步,把内存地址V中的变量值率先更新成了2。

4.线程1开始提交更新,首先进行A和地址V的实际值比较(Compare),发现A为1不等于V的实际值2,提交失败。
5.线程1重新获取内存地址V的当前值,并重新计算想要修改的新值。此时对线程1来说,A=2,B=3。这个重新尝试的过程被称为自旋,其实是一个for (;;) {}代码死循环。
6.这一次比较幸运,没有其他线程改变地址V的值。线程1进行Compare,发现A为2和地址V2的实际值是相等的。
7.线程1进行SWAP,把地址V的值替换为B,也就是3。

我们如何使用原子类:

比如AtomicInteger类的使用:

public class AtomicUse {
    static AtomicInteger atomicInteger = new AtomicInteger(0);

    public static void main(String[] args) {
       atomicInteger.getAndIncrement(); //表示i++
        atomicInteger.getAndAdd(1); //表示i++,这里变化delta

       atomicInteger.incrementAndGet(); //表示++i
       atomicInteger.addAndGet(1); //表示++i, 这里变化 delta
    }
}

CAS这么小巧还能达到原子性,线程安全,那么为什么不都用CAS呢,因为CAS也是有局限性的。

CAS的缺点:

1.CPU开销问题
在并发量比较高的情况下,如果许多线程反复尝试更新某一个变量,却又一直更新不成功,在长时间自旋下,会给CPU带来很大的压力。

2.不能保证多个变量的原子性
CAS机制所保证的只是一个变量的原子性操作,因为只能进行一个地址上的值比较,而不能保证整个代码块的原子性。如果需要保证多个变量共同进行原子性的更新,就不得不使用Synchronized了。

解决办法:使用AtomicRefrence来引用对象,将多个需要保证原子性的变量存入对象中,采用修改对象值,直接更新对象对对象实现原子性达到对多个变量实现原子性。

3.ABA问题
这是CAS机制最大的问题所在。线程1要将变量值从A修改为了B,如果V地址上值是A,则修改为B。在这个期间,而又来一个线程2,将V地址上的值由A变为了C,又重新改为了A。现在线程1来看这个值仍然是A,没有发生变化,线程1就会将值改为B。这就是ABA问题。

ABA问题解决方案:
加入版本戳:要求每个线程改这个值时,同时要修改版本戳。 Java提供了AtomicMarkableReference和AtomicStampedReference两个类来解决ABA问题。AtomicMarkableReference只关心变量是否被改过,而AtomicStampedReference还能观察到被改变了几次。

相关文章

  • 6、多线程

    synchronized关键字与CAS机制 synchronized:悲观锁CAS 机制:乐观锁当程序的并发量比较...

  • [转]Java中的CAS操作以及锁机制详解

    原文: Dyson~ - Java中的CAS操作以及锁机制详解 1. 关于CAS操作 CAS:Compare An...

  • (转载)Java CAS 和 synchronized 和 Lo

    原文链接:Java CAS 和 synchronized 和 Lock - CSDN博客 CAS 机制 适用场景:...

  • CAS知识摘抄(摘自:程序员小灰)

    什么是CAS? CAS是英文单词Compare And Swap的缩写,翻译过来就是比较并替换。 CAS机制当中使...

  • CAS机制

    还有就是这个atomic包,使用可以达到synchronized的效果,实现原理就是CAS了。 CAS也叫自旋锁,...

  • CAS机制

    加了同步锁之后,count自增的操作变成了原子性操作,所以最终输出一定是count=200,代码实现了线程安全。虽...

  • CAS机制

    CAS(Compare-And-Swap)算法保证数据操作的原子性。 CAS 算法是硬件对于并发操作共享数据的支持...

  • CAS机制

    当两个线程同时操作一个变量时,会拿到当前变量的值 如A = 0; A++此时,预期旧值则是A = 0 新值=1对...

  • CAS机制

    一、为什么需要CAS机制? 为什么需要CAS机制呢?我们先从一个错误现象谈起。我们经常使用volatile关键字修...

  • CAS机制

    原子操作 原子操作(atomic)就是不可分割的操作,在计算机中,就是指不会因为线程调度被打断的操作。通俗讲就是你...

网友评论

    本文标题:CAS机制

    本文链接:https://www.haomeiwen.com/subject/ghyjkltx.html