美文网首页
java - CAS底层原理及与synchronized的对比

java - CAS底层原理及与synchronized的对比

作者: 夹胡碰 | 来源:发表于2021-04-09 00:23 被阅读0次

    底层原理疑问

    CAS是比较并交换,AtomicInteger最终都是调用Unsafe.compareAndSwapInt方法进行实现,那Unsafe.compareAndSwapInt为什么是原子性的呢?它是怎么实现的?它的同步也是依赖于互斥吗?他与synchronized锁的底层实现有什么不同吗? 这两种同步方式的场景选择?

    解答

    1. Unsafe.compareAndSwapInt为什么是原子性的?

    他的原子性是由硬件指令实现的,底层硬件通过将 CAS 里的多个操作在硬件层面语义实现上,通过一条处理器指令保证了原子性操作。这些指令如下所示:
    (1)测试并设置(Tetst-and-Set)
    (2)获取并增加(Fetch-and-Increment)
    (3)交换(Swap)
    (4)比较并交换(Compare-and-Swap)
    (5)加载链接/条件存储(Load-Linked/Store-Conditional)

    前面三条大部分处理器已经实现,后面的两条是现代处理器当中新增加的。而且根据不同的体系结构,指令存在着明显差异。
    在IA64,x86 指令集中有cmpxchg指令完成 CAS 功能,在 sparc-TSO 也有 casa 指令实现,而在 ARM 和 PowerPC 架构下,则需要使用一对 ldrex/strex 指令来完成 LL/SC 的功能。在精简指令集的体系架构中,则通常是靠一对儿指令,如:load and reserve 和 store conditional 实现的,在大多数处理器上 CAS 都是个非常轻量级的操作,这也是其优势所在。

    2. 怎么实现的

    它采用了缓存锁定
    现在都是多核 CPU 处理器,每个 CPU 处理器内维护了一块字节的内存,每个内核内部维护着一块字节的缓存,当多线程并发读写时,就会出现缓存数据不一致的情况。
    此时,处理器提供:

    • 总线锁定
      当一个处理器要操作共享变量时,在 BUS 总线上发出一个 Lock 信号,其他处理就无法操作这个共享变量了。
      缺点很明显,总线锁定在阻塞其它处理器获取该共享变量的操作请求时,也可能会导致大量阻塞,从而增加系统的性能开销。

    • 缓存锁定
      后来的处理器都提供了缓存锁定机制,也就说当某个处理器对缓存中的共享变量进行了操作,其他处理器会有个嗅探机制,将其他处理器的该共享变量的缓存失效,待其他线程读取时会重新从主内存中读取最新的数据,基于 MESI 缓存一致性协议来实现的。
      现代的处理器基本都支持和使用的缓存锁定机制。

    3. 它的同步也是依赖于互斥吗?他与synchronized锁的底层实现有什么不同吗?

    它与synchronized最大的不同就是,CAS采用的缓存锁定,在没有竞争的时候没有额外的操作,当有竞争了才会有通知缓存失效机制。而synchronized是采用悲观互斥锁,即使没有线程竞争也会加上monitorentermonitorexit指令(不考虑jdk1.6之后的锁优化),会有线程的阻塞行为,影响性能。

    4. 场景选择

    CAS - 竞争小的情况,竞争过多造成自旋过多,造成cpu空跑
    synchronized - 竞争大的情况,竞争过小加悲观锁比较重

    参考

    1. 一文彻底搞懂CAS实现原理

    相关文章

      网友评论

          本文标题:java - CAS底层原理及与synchronized的对比

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