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

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


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)
全栈程序员-站长的头像全栈程序员-站长

相关推荐

  • 关于java代码生成器

    关于java代码生成器讲代码生成器之前先要说说模板,什么叫模板呢,举个例子吧,汇款单都见过吧,你不填写的那些内容都属于模板范畴说到这应该明白了吧,模板就是把共性提取出来反复使用,节约时间、工作量。。。。。那跟代码生成器有什么关系呢,思考一下在编程语言中所有的语言是不是都用共性或者说规范,这些都是固定不变的,在具体点,软件行业也是分主营业务的,比如OA、CRM、ERP、SCM等等,那么各个业务方向的软件

    2022年5月1日
  • Myabtis-plus中IService接口的使用

    Mybatis-plus的IService接口:IService接口解释,我们可以看到IService接口就是有一大堆方法接口。IService的使用:IService的使用需要另外两个接口的配合:baseMapper和ServiceImpl第一步:实现basemapper接口publicinterfaceAdminMapperextendsBaseMapper<Admin>{}第二步:编写service类publicinterfaceAdminServiceex

    2022年4月8日
  • HashMap底层实现原理_hadoop原理

    HashMap底层实现原理_hadoop原理Note:文章的内容基于JDK1.7进行分析,1.8做的改动文章末尾进行讲解。大家可以看一下:https://www.imooc.com/article/267756一、先来熟悉一下我们常用的HashMap1、概述HashMap基于Map接口实现,元素以键值对的方式存储,并且允许使用null建和null 值, 因为key不允许重复,因此只能有一个键为null,另外HashMap不…

    2022年9月10日
  • 无人机拍360全景操作细节详解

    对于制作360全景的小伙伴来说,无人机航拍是其中最重要的一课,无人机所展示的360全景地图是其他任何拍摄手段都不能比拟的,所以无人机拍摄也深受人们的欢迎。制作360全景的第一步肯定是拍摄照片我们先把无人机飞到空中大概50-70米的位置,把无人机相机调整到水平视角开始拍摄,朝一个方向横向旋转水平拍摄,拍摄一圈8张图片,每张照片20%左右的重合度。第一圈拍摄完成之后把无人机慢慢下降几米,或者调…

    2022年4月8日
  • 最详细的Vuex教程

    最详细的Vuex教程最详细的Vuex教程什么是Vuex?vuex是一个专门为vue.js设计的集中式状态管理架构。状态?我把它理解为在data中的属性需要共享给其他vue组件使用的部分,就叫做状态。简单的说就是data中需要共用的属性。引入Vuex(前提是已经用Vue脚手架工具构建好项目)1、利用npm包管理工具,进行安装vuex。在控制命令行中输入下边的命令就可以了。npminstallvuex

    2022年6月9日
  • linux中如何杀掉一个进程_linux系统杀死所有进程命令

    linux中如何杀掉一个进程_linux系统杀死所有进程命令方法一:sudokillall-9netease-cloud-music这种方法,必须要写全称。sudonetease-cloud-musicQStandardPaths:XDG_RUNTIME_DIRnotset,defaultingto’/tmp/runtime-root’05-23,14:29:48[Error][…

    2022年9月29日

发表回复

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

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