【CAS】CAS原理「建议收藏」

【CAS】CAS原理「建议收藏」CAS是所有原子类的底层原理,乐观锁主要采用CAS算法。CAS,比较并交换,是==JDK提供的非阻塞原子性操作,通过硬件保证比较-更新操作的原子性。==通常结合volatile保证共享变量的原子性。

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

1 CAS原理

CAS是所有原子类的底层原理,乐观锁主要采用CAS算法。

CAS,比较并交换,是JDK提供的非阻塞原子性操作,通过硬件保证比较-更新操作的原子性。 通常结合volatile保证共享变量的原子性。

思想:获取当前变量最新值A(预期值),然后进行CAS操作。此时如果内存中变量的值V(内存值V)等于预期值A,说明没有被其他线程修改过,我就把变量值改成B(更新值);如果不是A,便不再修改。

CAS操作利用CPU的特殊指令,由CPU保证原子性,完成一系列操作,不存在安全性问题。

CAS的变量需要用volatile修饰,以便在各线程之间保证可见。

CAS算法思想的使用场景

  • 乐观锁
  • 并发容器,例如ConcurrentHashMap
  • 原子类

2 AtomicLong中CAS使用分析

// 获取Unsafe实例
private static final Unsafe unsafe = Unsafe.getUnsafe();
// 获取变量value在内存中的偏移量
private static final long valueOffset;
static { 
   
    try { 
   
        valueOffset = unsafe.objectFieldOffset
            (AtomicLong.class.getDeclaredField("value"));
    } catch (Exception ex) { 
    throw new Error(ex); }
}

类加载过程中首先会加载Unsafe实例和valueOffset偏移量。valueOffset 表示变量在内存中的偏移地址,Unsafe根据内存偏移地址获取数据的预期值,然后进行CAS操作。为保证获取到值是最新值,因此变量通常用volatile修饰。

public final long getAndIncrement() { 
   
    return unsafe.getAndAddLong(this, valueOffset, 1L);
}
// Unsafe类
public final long getAndAddLong(Object var1, long var2, long var4) { 
   
    long var6;
    do { 
   
        // 通过对象地址和偏移量获取变量的最新值
        var6 = this.getLongVolatile(var1, var2);
        // 满足条件进行CAS操作
    } while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));

    return var6;
}

CAS方法底层c++源码实现

UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapLong(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong e, jlong x))
  UnsafeWrapper("Unsafe_CompareAndSwapLong");
  Handle p (THREAD, JNIHandles::resolve(obj));
  // 内存地址
  jlong* addr = (jlong*)(index_oop_from_field_offset_long(p(), offset));
  if (VM_Version::supports_cx8())
    // Atomic::cmpxchg原子性比较和替换
    return (jlong)(Atomic::cmpxchg(x, addr, e)) == e;
  else {
    jboolean success = false;
    ObjectLocker ol(p, THREAD);
    if (*addr == e) { *addr = x; success = true; }
    return success;
  }
UNSAFE_END

3 CAS的缺点

3.1 ABA问题

CAS是比较值,如果值相等则变换。此处可能存在这样的情况,线程1获取变量值为5,线程2将值改为10,线程3再将值改回5。对于线程1,变量的值没有变,但对于计数等后续操作是不正确的。

分析:ABA问题的产生是因为变量的状态值产生了环形变换。如果变量的值只能朝一个方向转化,便不会构成环形,不存在ABA问题。

解决方法:可以参考数据库乐观锁的处理,加版本号,变量更新时版本号会改变。通过比较版本号代替比较变量值。与集合的Fast-Fail机制类似,检查modCount值是否一致。

3.2 自旋时间长带来性能消耗

以AtomicLong为例,高并发场景下,如果线程一直无法进行CAS操作,内部是dowhile死循环,会一直自旋,消耗CPU。

自旋的理解

自旋是重试策略,既可以是乐观锁的重试策略,也可以使悲观锁的重试策略。例如AtimicLong很多方法都调用了getAndAddLong方法,内部便是利用了自旋+CAS操作。至于悲观锁,ReentrantLock的超时获取锁方法tryLock,便利用了for循环。

4 Unsafe

Unsafe是CAS核心类。Java无法直接访问底层操作系统,而是通过本地方法访问。JDK中Unsafe类,底层调用本地方法,提供硬件级别原子操作。

Unsafe类属于rt.jar包,使用bootstrap类加载器加载,而普通main函数类是使用AppClassLoader加载。Unsafe直接操作内存,因此叫不安全类,不能随意调用。

可以通过反射获取Unsafe实例。

Field field = Unsafe.class.getDeclareField("theUnsafe");
field.setAccessible(true);

本文案例代码位置:https://gitee.com/dtyytop/advanced-java

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/181293.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • pycharm专业版永久激活码-激活码分享

    (pycharm专业版永久激活码)JetBrains旗下有多款编译器工具(如:IntelliJ、WebStorm、PyCharm等)在各编程领域几乎都占据了垄断地位。建立在开源IntelliJ平台之上,过去15年以来,JetBrains一直在不断发展和完善这个平台。这个平台可以针对您的开发工作流进行微调并且能够提供…

    2022年3月30日
    1.9K
  • 青苹果论坛重新开放[通俗易懂]

    青苹果论坛重新开放[通俗易懂]青苹果论坛重新开放

    2022年7月3日
    73
  • 标注工具labelme_数据标注从哪里接单

    标注工具labelme_数据标注从哪里接单Labelme版本:3.11.2文章目录1.Labelme是什么?2.Labelme能干啥?3.Labelme安装要求4.Labelme安装方法5.Labelme使用方法6.Labelme常见问题7.Testing8.Developing9.将labelme打包成可执行文件10.致谢1.Labelme是什么?Labelme是一个图形界面的图像标注…

    2025年10月31日
    6
  • C语言read函数

    C语言read函数从文件中读取指定大小的字节函数read()语法:ssize_tread(intfd,void*buf,intcount)说明:read函数从指定的打开的文件fd中读取指定大小count的字节到从buf开始的缓冲区中.返回值:若读取失败则返回-1.读取成功则返回实际读取到的字节数,有两种情况:…

    2022年6月22日
    24
  • vue cil安装axios

    vue cil安装axiosVuecil安装axios1、安装axioscnpminstallaxios如果没有安装cnpm的可能安装不成功,这里列出安装cnpm,使用淘宝镜像。npminstall-gcnpm–registry=https://registry.npm.taobao.org2、使用方法2.1、首先在main.js配importaxiosfrom”axiosVue.prototype.$axios=axios好了就可以在项目中使用axios了。…

    2025年6月19日
    2
  • win10安装vs2008失败1935_LTE切换失败的解决方案

    win10安装vs2008失败1935_LTE切换失败的解决方案今天刚升级了WIN10系统,重新安装VS2008(项目需要),但是点击安装程序后,出现此应用程序需要.NET3.5(包括.NET2.0和3.0),如图:如果你选择“下载并安装此功能。Windows将从Windows更新中获取所需的文件并完成安装”,那么你就耐心等吧,下载的速度奇慢。现在讲解一下一种无需在线下载,只需要有Win10ISO文件即可本地安装.NETFramework3.5的方…

    2025年9月23日
    5

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号