Java——》CAS

这篇具有很好参考价值的文章主要介绍了Java——》CAS。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

推荐链接:
    总结——》【Java】
    总结——》【Mysql】
    总结——》【Redis】
    总结——》【Kafka】
    总结——》【Spring】
    总结——》【SpringBoot】
    总结——》【MyBatis、MyBatis-Plus】
    总结——》【Linux】
    总结——》【MongoDB】
    总结——》【Elasticsearch】

一、概念

CAS = Compare And Swap = 比较并交换

  • 先比较一下值是否与预期值一致,如果一致,交换,返回true
  • 先比较一下值是否与预期值一致,如果不一致,不交换,返回false

    CAS是一种在多线程环境中进行操作的原子指令。它的作用是对内存中的某个位置进行一次“乐观”的读取,并根据读取值与期望值是否相同来决定是否对该内存位置进行更新。
    CAS 操作是一种无锁的写入方式,允许多个线程并发的访问内存,并且通常比加锁操作的性能要高。
    Java中基于Unsafe的类提供了对CAS的操作的方法,JVM会帮助我们将方法实现CAS汇编指令。
    通过cmpxchg指令实现,单核就采用cmpxchg,多核需要追加前缀lock指令保证只有一个线程在执行当前CAS。

二、参数

参数 描述
内存位置(V) 要进行操作的内存位置
期望值(E) 将内存位置的值与期望值进行比较,如果相同则进行更新,否则不进行操作
更新值(U) 当期望值与内存位置的值相同时,CAS 操作会将内存位置的值更新为更新值

三、结果

CAS 操作的结果是一个布尔值,表示操作是否成功。
当期望值与内存位置的值相同时,CAS 操作会成功并返回 true,否则返回 false。

四、使用场景

  1. 实现各种无锁的同步机制,例如计数器、队列和栈等。
  2. 线程数较少、等待时间短可以采用自旋锁进行CAS尝试拿锁,较于synchronized高效

五、底层实现

最终回答:先从比较和交换的角度去聊清楚,在Java端聊到native方法,然后再聊到C++中的cmpxchg的指令,再聊到lock指令保证cmpxchg原子性

1、Java:Unsafe类中的native方法

CAS在Java层面就是Unsafe类中提供的一个native方法,成功返回true,失败返回false,如果需要重试策略,自己实现。

native是直接调用本地依赖库C++中的方法

/`
 * 如果变量的值为预期值,则更新变量的值,该操作为原子操作,如果修改成功则返回true
 * 
 * 4个参数:哪个对象,哪个属性的内存偏移量,oldValue预期值,newValue最终值
 */
public final native boolean compareAndSwapObject(Object o, long offset,
                                                 Object expected,
                                                 Object x);

public final native boolean compareAndSwapInt(Object o, long offset,
                                              int expected,
                                              int x);

public final native boolean compareAndSwapLong(Object o, long offset,
                                               long expected,
                                               long x);

2、C++:unsafe.cpp中的Unsafe_CompareAndSwapInt执行cmpxchg指令

本地已经下载hospot源码:\hotspot\src\share\vm\prims\unsafe.cpp
直接官网查看hospot源码:http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473/src/share/vm/prims/unsafe.cpp

UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))
  UnsafeWrapper("Unsafe_CompareAndSwapInt");
  oop p = JNIHandles::resolve(obj);
  jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);
  return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
UNSAFE_END

Java——》CAS,Java,java,CAS,原子性

3、linux:atomic_linux_x86.inline.hpp中的cmpxchg使用lock指令

本地已经下载hospot源码:\hotspot\src\os_cpu\linux_x86\vm\atomic_linux_x86.inline.hpp
直接官网查看hospot源码:http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473/src/os_cpu/linux_x86/vm/atomic_linux_x86.inline.hpp

cmpxchg是汇编指令,CPU硬件底层就支持cmpxchg。
cmpxchgl本身是原子性操作(不能再拆分的指令),但并不能保证原子性,所以需要判断当前系统是否为多核处理器,如果是多核,就添加lock前缀。
lock指令可以理解为CPU层面的锁,一般锁的粒度就是缓存行级别的锁,当然也有总线锁,但是成本太高,CPU会根据情况选择。

inline jint     Atomic::cmpxchg    (jint     exchange_value, volatile jint*     dest, jint     compare_value) {
  int mp = os::is_MP();// 内联函数,用来判断当前系统是否为多处理器(如果当前系统是多处理器返回1,否则返回0)
// __asm__代表汇编,直接操作硬件,执行cmpxchgl指令
__asm__ volatile (LOCK_IF_MP(%4) "cmpxchgl %1,(%3)" // 如果当前系统是多处理器(即mp值为1),则为cmpxchg指令添加lock前缀,否则不加lock前缀
                    : "=a" (exchange_value)
                    : "r" (exchange_value), "a" (compare_value), "r" (dest), "r" (mp)
                    : "cc", "memory");
  return exchange_value;
}

Java——》CAS,Java,java,CAS,原子性

六、基于CAS实现的类

参考链接:
Java——》Unsafe源码分析
Java——》AtomicInteger源码分析

  • AtomicInteger
  • LongAdder
  • ReentrantLock

Java——》CAS,Java,java,CAS,原子性

七、优点

什么是非阻塞式的呢?其实就是一个线程想要获得锁,对方会给一个回应表示这个锁能不能获得。在资源竞争不激烈的情况下性能高,相比synchronized重量锁会进行比较复杂的加锁,解锁和唤醒操作。

CAS是一种乐观锁,而且是一种非阻塞的轻量级的乐观锁,不会挂起线程。

八、缺点

1、ABA问题

ABA不一定是问题!因为一些只存在 ++,–的这种操作,即便出现ABA问题,也不影响结果!

(1)现象

Java——》CAS,Java,java,CAS,原子性

(2)解决方案

在修改value同时追加 版本号

(3)示例

JUC下提供的AtomicStampedReference(同时判断值和版本号)
Java——》CAS,Java,java,CAS,原子性

public static void main(String[] args) {
    AtomicStampedReference<String> reference = new AtomicStampedReference<>("AAA",1);

    String oldValue = reference.getReference();
    int oldVersion = reference.getStamp();

    boolean b = reference.compareAndSet(oldValue, "B", oldVersion, oldVersion + 1);
    System.out.println("修改1版本的:" + b);

    boolean c = reference.compareAndSet("B", "C", 1, 1 + 1);
    System.out.println("修改2版本的:" + c);
}

2、如果cas失败(自旋)次数过多占用CPU资源

(1)现象

while循环,cas一直没成功,会一直进行自旋,会额外的占用大量的CPU资源
Java——》CAS,Java,java,CAS,原子性

Q:为什么会占用CPU资源?
A:因为CAS不会挂起线程,会让CPU一致调度当前线程执行CAS直到成功。

(2)解决方案

不同场景有不同的处理方案

方案 特点 具体实现
synchronized的实现方式 自适应自旋锁 如果cas失败(自旋)次数过多(超过指定次数),就挂起线程(WAITING),避免占用CPU过多的资源
LongAdder的实现方式 分段锁 在CAS一次失败后,将这个操作暂存起来,后面需要获取结果时,将暂存的操作全部执行,再返回最后的结果

Q:LongAdder?
A:基于类似 分段锁 的形式去解决(要看业务,有限制的)
传统的AtmoicLong是针对内存中唯一的一个值去++
现在的LongAdder在内存中搞了好多个值,多个线程去加不同的值,如果自增失败,将失败的信息添加到Cell[],当需要结果时,将所有值累加再返回。

3、只能保证一个数据的安全

(1)现象

无法像synchronized一样锁住一段代码

(2)解决方案

ReentrantLock基于AQS实现,AQS基于CAS的方式实现了锁的效果。

九、CPU 实现原子指令

CPU 实现原子指令有 2 种方式:

  • 1、通过总线锁定来保证原子性
  • 2、通过缓存锁定来保证原子性

1、通过总线锁定来保证原子性

所谓总线锁定就是使用处理器提供的一个 LOCK# 信号,当一个处理器在总线上输出此信号时,其他处理器的请求将被阻塞住,那么该处理器可以独占共享内存。但是该方法成本太大。因此有了下面的方式。

2、通过缓存锁定来保证原子性

所谓 缓存锁定 是指内存区域如果被缓存在处理器的缓存行中,并且在 Lock 操作期间被锁定,那么当他执行锁操作写回到内存时,处理器不在总线上声言 LOCK# 信号,而是修改内部的内存地址,并允许他的缓存一致性机制来保证操作的原子性,因为缓存一致性机制会阻止同时修改两个以上处理器缓存的内存区域数据(这里和 volatile 的可见性原理相同),当其他处理器回写已被锁定的缓存行的数据时,会使缓存行无效。

注意:有两种情况下处理器不会使用缓存锁定:文章来源地址https://www.toymoban.com/news/detail-745881.html

  1. 当操作的数据不能被缓存在处理器内部,或操作的数据跨多个缓存行时,则处理器会调用总线锁定。
  2. 有些处理器不支持缓存锁定,对于 Intel 486 和 Pentium 处理器,就是锁定的内存区域在处理器的缓存行也会调用总线锁定。

十、CAS保证原子性示例

private static AtomicInteger count = new AtomicInteger(0);

public static void main(String[] args) throws InterruptedException {
    Thread t1 = new Thread(() -> {
        for (int i = 0; i < 100; i++) {
            count.incrementAndGet();
        }
    });
    Thread t2 = new Thread(() -> {
        for (int i = 0; i < 100; i++) {
            count.incrementAndGet();
        }
    });
    t1.start();
    t2.start();
    t1.join();
    t2.join();
    System.out.println(count);
}

到了这里,关于Java——》CAS的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java多线程系列——CAS机制

    在并发编程的世界里,线程安全是个不得不面对的问题,而CAS(Compare-And-Swap,比较并交换)正是保障并发安全中一种非常关键的机制。本文将深入剖析Java多线程环境下的CAS机制,包括其工作原理、实现方式、面临的问题以及相关的优化策略,力求为读者带来全面的了解。  

    2024年02月22日
    浏览(45)
  • 【Java多线程进阶】CAS机制

    前言 CAS指的是Compare-And-Swap(比较与交换),它是一种多线程同步的技术,常用于实现无锁算法,从而提高多线程程序的性能和扩展性。本篇文章具体讲解如何使用 CAS 的机制以及 CAS 机制带来的问题。 目录 1. 什么是CAS? 2. CAS的应用 2.1 实现原子类 2.2 实现自旋锁 3. CAS的ABA问

    2024年02月10日
    浏览(39)
  • 【Java 并发编程】CAS 原理解析

    悲观锁 的原理是每次实现数据库的增删改的时候都进⾏阻塞,防⽌数据发⽣脏读。 乐观锁 的原理是在数据库更新的时候,⽤⼀个 version 字段来记录版本号,然后通过⽐较是不是⾃⼰要修改的版本号再进⾏修改。这其中就引出了⼀种⽐较交换的思路来实现数据的⼀致性,事实

    2024年02月06日
    浏览(39)
  • 深入浅出Java多线程(十):CAS

    大家好,我是你们的老伙计秀才!今天带来的是[深入浅出Java多线程]系列的第十篇内容:CAS。大家觉得有用请点赞,喜欢请关注!秀才在此谢过大家了!!! 在多线程编程中,对共享资源的安全访问和同步控制是至关重要的。传统的锁机制,如synchronized和ReentrantLock等

    2024年03月11日
    浏览(54)
  • java JUC并发编程 第六章 CAS

    第一章 java JUC并发编程 Future: link 第二章 java JUC并发编程 多线程锁: link 第三章 java JUC并发编程 中断机制: link 第四章 java JUC并发编程 java内存模型JMM: link 第五章 java JUC并发编程 volatile与JMM: link 第六章 java JUC并发编程 CAS: link 第七章 java JUC并发编程 原子操作类增强: link 第八章

    2024年02月10日
    浏览(48)
  • Java CAS和AQS的实现原理

    CAS(Compare And Swap)是一种并发控制机制,用于解决多线程并发访问共享资源时的数据一致性问题。 在Java中,CAS操作通常使用Atomic类来实现。例如,可以使用java.util.concurrent.atomic.AtomicInteger类来实现对整数类型的原子操作。Atomic类提供了一系列的原子操作方法,例如getAndAdd、

    2024年02月06日
    浏览(55)
  • 【Java】常见锁策略 && CAS机制 &&锁优化策略

    在本文会详细介绍各种锁策略、CAS机制以及锁优化策略 不仅仅局限于Java,任何和锁相关的话题,都可能会涉及到下面的内容。 这些特性主要是给锁的实现者来参考的. 普通的程序猿也需要了解一些, 对于合理的使用锁也是有很大帮助的 乐观锁 :预测到程序中遇到冲突的可能

    2024年04月22日
    浏览(37)
  • Java——并发编程(CAS、Lock和AQS)

    答: Lock 接口比同步方法和同步块提供了 更具扩展性的锁操作 。他们允许更灵活的结构,可以具有完全不同的性质,并且可以支持多个相关类的条件对象。 可以使锁更公平; 可以使线程在等待锁的时候响应中断; 可以让线程尝试获取锁,并在无法获取锁的时候立即返回或

    2024年02月06日
    浏览(55)
  • Java多线程(3)---锁策略、CAS和JUC

    目录 前言 一.锁策略 1.1乐观锁和悲观锁 ⭐ 两者的概念 ⭐实现方法 1.2读写锁  ⭐概念 ⭐实现方法 1.3重量级锁和轻量级锁 1.4自旋锁和挂起等待锁 ⭐概念 ⭐代码实现 1.5公平锁和非公平锁 1.6可重入锁和不可重入锁 二.CAS 2.1为什么需要CAS 2.2CAS是什么 ⭐CAS的介绍 ⭐CAS工作原理

    2024年02月13日
    浏览(70)
  • 【Java基础】CAS (Compare And Swap) 操作

    关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 ,擅长java后端、移动开发、人工智能等,希望大家多多支持。 我们继续总结学习 Java基础知识 ,温故知新。 CAS其实就是Compare And Swap的一个缩写,顾名思义就是比较并交换,

    2024年02月13日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包