CAS专篇
前言
日常编码过程中,基本不会直接用到 CAS 操作,都是通过一些JDK 封装好的并发工具类来使用的,在 java.util.concurrent 包下。但是面试时 CAS 还是个高频考点,所以呀,你还不得不硬着头皮去死磕一下这块的技能点,总比一问三不知强吧?
一、为什么要用无锁?
我们一想到在多线程下保证安全的方式头一个要拎出来的肯定是锁,不管从硬件、操作系统层面都或多或少在使用锁。锁有什么缺点吗?当然有了,不然 JDK 里为什么出现那么多各式各样的锁,就是因为每一种锁都有其优劣势。

使用锁就需要获得锁、释放锁,CPU 需要通过上下文切换和调度管理来进行这个操作,对于一个 独占锁 而言一个线程在持有锁后没执行结束其他的哥们就必须在外面等着,等到前面的哥们执行完毕 CPU 大哥就会把锁拿出来其他的线程来抢了(非公平)。锁的这种概念基于一种悲观机制,它总是认为数据会被修改,所以你在操作一部分代码块之前先加一把锁,操作完毕后再释放,这样就安全了。其实在 JDK1.5 使用 synchronized 就可以做到 。

但是像上面的操作在多线程下会让 CPU 不断的切换,非常消耗资源,我们知道可以使用具体的某一类锁来避免部分问题。那除了锁的方式还有其他的吗?当然,有人就提出了无锁算法,比较有名的就是我们今天要说的 CAS(compare and swap),和锁不同的是它是一种乐观的机制,它认为别人去拿数据的时候不会修改,但是在修改数据的时候去判断一下数据此时的状态,这样的话 CPU 不会切换,在读多的情况下性能将得到大幅提升。当前我们使用的大部分 CPU 都有 CAS 指令了,从硬件层面支持无锁,这样开发的时候去调用就可以了。不论是锁还是无锁都有其优劣势。
二、CAS
1、CAS 的概述
CAS (Compare And Swap,比较与交换),是一种支持硬件层次的原子性操作的经典无锁算法。无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(Non-blocking Synchronization)。许多CAS的操作是自旋的:如果操作不成功,会一直重试,直到操作成功为止。
CAS算法涉及到三个操作数:
需要读写的内存位置(V)
进行比较的旧预期值(A)
准备写入的新值(B)
2、CAS 的原理
CAS的原理,具体修改数据过程如下:
- 用CAS操作数据时,将原始值和要修改的值一并传递给方法;
- 比较当前内存中的目标变量值与传进去的原始值是否相同?
- 如果相同,表示内存中的目标变量值没有被其他线程修改过,可以直接修改内存种的目标变量值;
- 如果不同,那么证明内存种的目标变量值已经被其他线程修改过,则修改失败,CAS会做自旋操作,不断循环重试。
3、CAS 会产生并发安全问题吗?
实际应用中这种情况不会发生。例如:Java中的CAS就体现在 sun.misc.Unsafe 类中的各个方法里。
//下面是Unsafe类中的方法 public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
可以看到,这些都是native本地方法,调用过程中,JVM会借助C来调用CPU底层指令实现硬件级别的CAS比较和交换。 看出这是一种完全依赖于底层硬件的操作,通过它来实现原子操作,所以并不会带来并发问题。
注意,CAS是一种系统原语,原语属于操作系统用于范畴,是由若干条指令组成的,用于完成某个功能的一个过程,并且原语的执行必须是连续的,在执行过程中不允许被中断,也就是说CAS是一条CPU的原子指令,不会造成所谓的数据不一致问题。
三、Java中的CAS
1、代码实例
我们使用java.util.concurrent.atomic.AtomicInteger类中的compareAndSet()方法,此方法的作用是:将原始值与期待值进行比较,如果相等则将原始值设为要修改的新值(让新值成为原始值),并返回true;不相等则修改失败,返回false。
import java.util.concurrent.atomic.AtomicInteger;
public class CASDemo {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(5); //初始化原子类的值为0
//将初始值0与预期值5进行比较,如果相等,则将预期值5改为新值2020
atomicInteger.compareAndSet(5,<

本文详细介绍了CAS(CompareAndSwap)无锁算法的工作原理、使用场景及其在Java中的实现,包括AtomicInteger类的源码分析。同时,文章探讨了CAS的优缺点,如CPU开销大、只能保证单个变量原子操作以及ABA问题,并给出了ABA问题的解决方案。最后,对比了CAS与synchronized在不同并发情况下的适用性。
最低0.47元/天 解锁文章
814

被折叠的 条评论
为什么被折叠?



