java并发之CAS(Compare and swap)

news2024/11/26 17:36:19

1. 简介

CAS的底层调用native方法,最终是利用CPU的一个特殊指令,该指令由CPU保证了原子性,而且包含多个操作,比如先比较再更新。

原理:

  • (1)需要读写的内存值(V)、原值(A)和新值(B)。如果V的值与原值A相匹配,那么把B设置给V,否则处理器不做任何操作。
  • (2)无论哪种情况,都返回V内存值。
  • (3)原子类里,当失败时,就一直循环,直到成功。

(1)、(2)是在CPU和内存的层面来说的,(3)是在Java层面说的

比如 AtomicInteger中的源码中,就使用到了 CAS:

public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    do {
        var5 = this.getIntVolatile(var1, var2);
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

    return var5;
}

2. CAS的等价代码

自己实现CAS:

/**
  *      模拟CAS操作,等价代码
 */
public class SimulatedCAS {
    private int value;

    /**
     * 模拟CAS操作
     * @param expectedValue 预期值
     * @param newValue 修改值
     * @return 原值
     */
    public synchronized int compareAndSwap(int expectedValue, int newValue) {
        int oldValue = value;

        if (oldValue == expectedValue) {
            value = newValue;
        }
        return oldValue;
    }
}

在多线程中调用上面自己实现的CAS:

/**
  *      两个线程竞争,其中一个修改落败
 */
public class TwoThreadCompetition implements Runnable{
    private int value;

    /**
     * 模拟CAS操作
     * @param expectedValue 预期值
     * @param newValue 修改值
     * @return 旧值
     */
    public synchronized int compareAndSwap(int expectedValue, int newValue) {
        int oldValue = value;

        if (oldValue == expectedValue) {
            value = newValue;
            System.out.println(Thread.currentThread().getName()+"将值从"+oldValue+"修改成"+newValue);

        }
        return oldValue;
    }

    //主函数
    public static void main(String[] args) throws InterruptedException {
        TwoThreadCompetition r = new TwoThreadCompetition();
        r.value=0;//默认值
        Thread thread1 = new Thread(r,"线程1");
        thread1.start();
        thread1.join();
        System.out.println(r.value);
        Thread thread2 = new Thread(r,"线程2");
        thread2.start();
        thread2.join();
        System.out.println(r.value);
    }

    @Override
    public void run() {
        compareAndSwap(0,1);
    }
}

image-20230611211632613

打印结果显示,只有第一个线程对 value 进行了修改,第二个线程没有修改,符合CAS的逻辑。

3. 应用场景

(1)乐观锁

乐观锁的实现就是利用CAS原理,比如数据库在修改时,是利用version字段来判断是否应该执行修改,而不是加锁。

(2)并发容器

如ConcurrentHashMap类中的put()方法

(4)原子类

这里以 AtomicInteger 的 getAndIncrement()方法 为例剖析如何利用CAS实现原子性的

  • AtomicInteger加载Unsafe工具,用来直接操作内存数据
  • 用Unsafe工具来实现底层操作
  • 用volatile修饰value字段,保证可见性
上面使用到了 Unsafe 类,那么他是个什么呢?
  • Unsafe是CAS的核心类。Java一般无法直接访问底层操作系统,而是通过本地(native)方法来访问。不过尽管如此,JVM还是开了一个后门,JDK中有一个类Unsafe,它提供了硬件级别的原子操作。
  • valueOffset表示的是变量值在内存中的偏移地址,因为Unsafe就是根据内存偏移地址获取数据的原值的,这样我们就能通过unsafe来实现CAS了
总结:

总之,getAndIncrement() 的实现原理是:先获取 value 在内存中的地址,然后使用该地址拿到原值,再调用 compareAndSwapInt 方法,将内存地址value,原值,和新值传进去,执行 CAS 方法,compareAndSwapInt 是 native 方法,由c++实现,如下

上面的 C++ 代码可以看出:方法中先想办法拿到变量value在内存中的地址;通过Atomic::cmpxchg实现原子性的比较和替换,其中参数x是即将更新的值,参数e是原内存的值。至此,最终完成了CAS的全过程。

4 CAS的缺点

(1)ABA 问题

什么是ABA?

假若一个变量初次读取是A,在compare阶段依然是A,但其实可能从读取A到后面的compare这段过程里,它先被改为B,再被改回A,而CAS是无法意识到这个问题的。CAS只关注了比较那一刻的值是否A,而无法清楚在此过程中变量的变更明细,这就是所谓的ABA漏洞。

如何解决ABA问题

可以采用数据库的乐观锁方式,采用version版本号,每次修改就给version版本号+1

(2)自旋时间过长

do-while的自旋循环,如果多线程竞争激烈的情况下,锁一直拿不到,他就会一直自旋,消耗CPU性能。

点我扫码关注微信公众号

文章来源:Java并发之CAS(Compare and swap)


个人微信:CaiBaoDeCai

微信公众号名称:Java知者

微信公众号 ID: JavaZhiZhe

谢谢关注!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/643510.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Kafka】Docker安装kafkajava kafka api

内容目录 一、安装zookeeper1 拉取镜像2 创建network3 启动容器 二、安装kafka1 拉取kafka镜像2 启动kafka容器3 创建topic4 创建生产者5 创建消费者 三、kafka的java api1 producer2 消费者 docker依赖于zookeeper,首先安装zookeeper 一、安装zookeeper 1 拉取镜像…

[PyTorch][chapter 40][数据增强]

前言: 深度学习对数据量要求非常大, 我们通常会遇到图像的数据集比较小,影响Train效果。 这个时候可以通过transformer 方法,增加图像的多样性,达到数据 增强的效果。 transformer 不会单独使用,通常和其它torch 其他类一起使用 transformer 常用方法…

可视管理 数字孪生智慧隧道一体化管控平台

前言 交通是国家发展的关键,四通八达的交通路线,对国家经济、社会等方面的发展起着至关重要的作用。 建设背景 随着社会经济的持续发展与城市化进程的平稳推进,我国公路工程规模逐步扩大,公路工程建设直接影响着城市未来发展与…

Vue 报错 error:0308010C:digital envelope routines::unsupported

症状 Vue 报错error:0308010C:digital envelope routines::unsupported 原因 出现这个错误是因为 node.js V17版本中最近发布的OpenSSL3.0, 而OpenSSL3.0对允许算法和密钥大小增加了严格的限制,可能会对生态系统造成一些影响. 解决方法 方法1 打开终端&#x…

React 应用 Effect Hook 函数式中操作生命周期

React Hook入门小案例 在函数式组件中使用state响应式数据给大家演示了最简单的 Hook操作 那么 我们继续 首先 Hook官方介绍 他没有破坏性是完全可选的 百分比兼容 也就说 我们一起的 类 class的方式也完全可以用 只要 react 16,8以上就可以使用 Hook本身不会影响你的react的理…

ESXi 7.0 U3m Hitachi (日立) 定制版 OEM Custom Installer CD

VMware ESXi 7.0 Update 3m - 领先的裸机 Hypervisor (All OEM Customized Installer CDs) ESXi 7.0 U3m Standard (标准版) ESXi 7.0 U3m Dell (戴尔) 定制版 OEM Custom Installer CD ESXi 7.0 U3m HPE (慧与) 定制版 OEM Custom Installer CD ESXi 7.0 U3m Lenovo (联想) 定…

4.单表查询

SQL句子中语法格式提示: 1.中括号([])中的内容为可选项; 2.[,...]表示,前面的内容可重复; 3.大括号({})和竖线(|)表示选择项,在选择…

chatgpt赋能python:Python怎么导入第三方库

Python怎么导入第三方库 如果你是Python开发者,你一定会使用各种第三方库来加速你的开发过程。这些库可能是Python标准库之外的代码,或由其他人编写的自定义代码。使用这些库可以让你的开发更高效、更易于管理,并且可以避免重复造轮子。 但…

RabbitMQ虚拟主机无法启动的原因和解决方案

RabbitMQ虚拟主机无法启动的原因和解决方案 摘要: RabbitMQ是一个广泛使用的开源消息代理系统,但在使用过程中可能会遇到虚拟主机无法启动的问题。本文将探讨可能导致该问题的原因,并提供相应的解决方案,以帮助读者解决RabbitMQ虚…

Learning C++ No.31 【线程库实战】

引言: 北京时间:2023/6/11/14:40,实训课中,实训场地有空调,除了凳子坐着不舒服之外,其它条件都挺好,主要是我带上了我自己的小键盘,并且教室可以充电,哈哈哈&#xff0c…

在做自动化测试之前你需要知道的

B站视频教程:Python自动化测试:7天练完这60个实战项目,年薪过35w。 什么是自动化测试? 做测试好几年了,真正学习和实践自动化测试一年,自我感觉这一个年中收获许多。一直想动笔写一篇文章分享自动化测试实践…

信息系统管理工程师-学习笔记1-信息化知识

考点1 信息与信息系统 信息的概念 信息的定义: 是有别与物质与能量的第三种东西,是对事物运动状态或存在方式的不确定行的描述 信息是按特定方式组织在一起的客体属性的集合,具有超出这些客体属性本身之外的价值两层次 1.本体论层次 : 纯客观的层次,只与客体本身的因素有关,与主…

python cv2的一些操作,如膨胀,画线,滤波等

目录 0. cv2简介1. 打开摄像头2. 画图,画线3. 滤波4. 获取角点5. 梯度边缘6. 图形匹配7. 形态学变化-膨胀腐蚀8. 二值化阈值10. 总结 0. cv2简介 在这里先简单介绍一下cv2吧。 cv2 是 OpenCV Python 库的主要模块,提供了许多图像处理和计算机视觉方面的函数和工具。…

vue2组件通信

父传子 传递静态或动态 Prop <!-- 传入静态值 --> <blog-post title"hai hai hai"></blog-post><!-- 传入变量值 --> <blog-post :title"info.title"></blog-post>传入一个对象的所有 property 数据 post: {id: 1…

进程管道:popen函数实例

基础知识 可能最简单的在两个程序之间传递数据的方法就是使用popen和pclose函数了。它们的原型如下所示&#xff1a; #include <stdio.h>FILE *popen(const char *command, const char *type);int pclose(FILE *stream); 1&#xff0e;popen函数 popen函数允许一个程…

因为Json,controller方法单参数 导致脑袋短路

对于单参数方法&#xff0c; 一直喜欢用parameter方式。今天不知道为啥&#xff0c;就想用Json方式&#xff0c;然后无法直接传递。各种自我怀疑&#xff0c;然后尝试。 突然醒悟过来&#xff0c;Json方式是key/value模式&#xff0c;单参数String类型&#xff0c;没有key。必…

TreeMap源码

介绍 如果我们希望Map可以保持key的大小顺序时&#xff0c;就需要利用TreeMap。底层使用了红黑树&#xff0c;左子树总小于root&#xff0c;右子树总大于root&#xff0c;具有很好的平衡性,操作速度达到log(n)。 TreeMap 相比于HashMap多实现了了NavigableMap接口&#xff08…

5. SpringCloudAlibab 集成 gateway

一、什么是 Spring Cloud Gateway 1、网关简介 网关作为流量的入口&#xff0c;常用的功能包括路由转发&#xff0c;权限校验&#xff0c;限流等等。 SpringCloud Gateway是 Spring Cloud 官方推出的第二代网关框架&#xff0c;定位取代 Netflix Zuul。相对Zuul来说&#xf…

【多线程】原子引用ABA问题

目录 一、代码示例二、执行结果截图三、说明四、AtomicStampedReference使用4.1 代码示例4.2 截图 一、代码示例 package com.learning.atomic;import lombok.extern.slf4j.Slf4j; import java.util.concurrent.atomic.AtomicReference; /*** Author wangyouhui* Description …

2023年软考-高级信息系统项目管理工程师考试大纲

高级信息系统项目管理工程师考试大纲 2023年软考高级信息系统项目管理工程师考试大纲已于2023年5月出版。您可以在 中国计算机技术职业资格网 上找到更多关于考试的信息 。 信息系统项目管理师是对从事信息系统项目管理工作的专业技术人员基本理论和实践能力的综合考核,该专业…