剑指Offer(锁)——CAS(Compare and Swap)

这是一种高效实现线程安全性的方法

  • 它支持原子更新操作,适用于计数器,序列发生器等场景。
  • 属于乐观锁机制,号称lock-free(无锁),但是实际上还是有一些底层锁的。
  • CAS操作失败的时候,是由开发者决定是继续尝试还是执行别的操作。

CAS操作的思想

它包含三个操作数——内存位置(V)、预期原值(A),和新值(B)。

在比较的时候,由内存位置和预期原值去进行对比,如果是相同的,就会将内存位置的值更新为新值否则处理器不做任何操作,这里内存位置的值,指的就是主内存的值。

举一个例子:

当一个CAS操作想要修改共享变量的值,要完成这个操作,需要先取出共享变量的值赋值给A,然后基于A的基础进行计算得到新值B,当执行完毕,需要更新共享变量的时候,就可以调用CAS方法去更新变量的值了。

用一个代码来更加详细的说明一下:

在这里插入图片描述
然后我们还用javap看一下字节码的操作指令
在这里插入图片描述

可以发现add方法的value++操作被分成了两个部分,在getfield操作中,它将value的值加载进了当前线程的工作内存中,紧接着,执行iadd操作,也就是+1,最后执行putfield,将value的值写入主内存中。因为我们使用了volatile,所以可以保证线程之间的可见性,但是无法保证原子性,所以在多线程下是不安全的,该如何解决呢?

这里的解决方法是很简单的,有两种:

  1. 使用synchronized,加互斥锁

在这里插入图片描述

  1. 使用Atomic原子操作

在这里插入图片描述

总结:

  1. CAS在大多数情况下对于开发者来说是透明的,因为我们不需要操作指令重排

  2. JUC下的atomic包提供了常用的原子性数据类型以及引用、数组。等相关原子类型和更新操作工具,是很多线程安全程序的首选。

  3. Unsafe虽然有CAS操作,但是还是因为能够任意操作内存地址读写而又隐患存在

  4. 如果是jdk9,我们可以使用Variable Handle API替换Unsafe

不过CAS操作也是有缺点的:

  1. 如果循环时间够长,那么开销就会很大
  2. 只能保证一个共享变量的原子操作
  3. ABA问题(解决方法:使用AtomicStampedReference基于变量版本控制检查是否发生值的替换)
发布了296 篇原创文章 · 获赞 53 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_41936805/article/details/103396678