首页 > 其他分享 >volatile关键字和CAS的原子性操作

volatile关键字和CAS的原子性操作

时间:2023-09-26 12:22:38浏览次数:38  
标签:Thread 修改 CAS 关键字 casObject 线程 volatile stamp

volatile 关键字

volatile 是 Java 中的关键字,用于修饰变量。它的作用是确保对被修饰变量的读写操作具有可见性和顺序性。

可见性:当一个线程修改了 volatile 变量的值,其他线程可以立即看到最新的值。这是因为 volatile 变量在修改时会强制将最新的值刷新到主内存中,并在读取时从主内存中获取最新的值。

顺序性:在使用 volatile 变量进行读写操作时,编译器和处理器会禁止指令重排序,保证了操作的顺序性。

不保证原子性

CAS(Compare and Swap)

CAS 是一种无锁的原子操作,用于实现多线程环境下的并发控制。CAS 操作包含三个参数:内存位置(变量地址)、期望值和新值。它的执行过程如下:

首先,它会比较内存位置中的值与期望值是否相等。

如果相等,则将新值写入该内存位置。

如果不相等,则表示其他线程已经修改了该内存位置,CAS 操作失败,需要重新尝试。

CAS 操作是一种乐观并发控制方式,相对于传统的使用锁的悲观并发控制方式,它可以提供更高的并发性能。

CAS 存在的 ABA 问题

ABA 问题指的是在使用 CAS 进行比较时,可能出现值从 A 变为 B,然后再次变回 A 的情况。这种情况下,CAS 操作在比较时可能会成功,尽管中间的操作已经改变了变量的值。这可能引发一些潜在的问题。

此时CAS认为期望值和新值相等,会误认为可以修改变量,但其实变量已经发生了修改,CAS的原子性操作已无法保证!

为了应对ABA问题,Java 提供了 AtomicStampedReference 和 AtomicMarkableReference 类

    @Test
    void CASTest(){

        AtomicStampedReference<String> casObject = new AtomicStampedReference<>("A", 0);

        // 线程1:尝试修改值
        Thread thread1 = new Thread(() -> {
            int stamp = casObject.getStamp(); // 获取当前标记值
            String oldValue = casObject.getReference(); // 获取当前引用值

            try {
                Thread.sleep(1000); // 为了模拟thread2线程的修改
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // 尝试使用 CAS 修改值
            boolean success = casObject.compareAndSet(oldValue, "C", stamp, stamp + 1);

            log.info("Thread 1 - CAS operation success: " + success);
        });

        // 线程2:修改值为初始值
        Thread thread2 = new Thread(() -> {
            int stamp = casObject.getStamp(); // 获取当前标记值
            String oldValue = casObject.getReference(); // 获取当前引用值

            // 将值从 A 修改为 B
            casObject.compareAndSet(oldValue, "B", stamp, stamp + 1);
            System.out.println("Thread 1: Value changed to B");

            // 将值从 B 修改回 A
            stamp = casObject.getStamp();
            casObject.compareAndSet("B", "A", stamp, stamp + 1);
            System.out.println("Thread 1: Value changed back to A");

            log.info("Thread 2 - Value changed to initial value");
        });

        thread1.start();
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        /*
            最后输出结果为 Final value: A
            这说明thread1线程在修改变量casObject时保证了原子性,否则就会输出Final value: C
            这是因为AtomicStampedReference不仅会比较期望值,还会比较标记值stamp,当两者都相等时才会执行Swap行为修改变量
         */
        log.info("Final value: " + casObject.getReference());
    }

 由于修改AtomicStampedReference对象时,你可以在stamp标记位做标识,如果标记位的值发生了变化,那么CAS操作就会被取消,即有效避免了ABA问题。

 

关于CAS的一些思考

CAS操作允许多个线程同时访问共享资源,但只有一个线程能成功地执行更新操作。

CAS是一种乐观锁的原子操作,是因为它相对于synchronized等悲观锁实现,CAS操作不需要加锁和解锁过程,因此减少了线程间的竞争和阻塞,提高了并发性能。悲观锁在占据资源时,其他线程访问该资源,则需要等待锁释放资源+竞争锁。

悲观锁担心资源很容易被修改,为了避免多个线程看到的资源不一致,因此并发访问在锁持有资源的时候就成了串行访问。

乐观锁认为只有资源被修改时,才会发生并发安全问题,因此直接允许多线程访问资源,当更新资源时,工作内存中资源的期望值与主内存中资源的值不相等时(线程访问的资源是从主内存拷贝到线程工作区间的资源,因此才能比较),才会做出限制。

标签:Thread,修改,CAS,关键字,casObject,线程,volatile,stamp
From: https://www.cnblogs.com/ashet/p/17729821.html

相关文章

  • requests 响应头部转json时报错TypeError: Object of type CaseInsensitiveDict is n
    前言requests响应头部在转json时,想格式化输出,结果报错TypeError:ObjectoftypeCaseInsensitiveDictisnotJSONserializable报错详情示例代码importrequestsimportjson#上海悠悠wx:283340479#blog:https://www.cnblogs.com/yoyoketang/r=requests.get('htt......
  • 一、简易搭建本地CAS服务端
    CAS服务端war包下载https://repo1.maven.org/maven2/org/apereo/cas/cas-server-webapp-tomcat/5.3.14/可使用迅雷下载cas-server-webapp-tomcat-5.3.14.war,速度很快将wab包放到本地tomcat的webapps下D:\tomcat\apache-tomcat-8.5.63\webapps\cas\WEB-INF\classes\servic......
  • PIM-DM:Protocol Independent Multicast-Dense Mode
    建立连接的过程1、hello报文(30s)2、扩展3、prune修剪4、断言机制5、嫁接6、刷新(105s)......
  • [开源]-OpenCASCADE-IMGUI
    [开源]-OpenCASCADE-IMGUI1IMGUIImGui是一个用于C++的用户界面库,跨平台、无依赖,支持OpenGL、DirectX等多种渲染API,是一种即时UI(ImmediateModeUserInterface)库,保留模式与即时模式的区别参考保留模式与即时模式。ImGui渲染非常快,但界面上有大量的数据集需要渲染可能会有一些......
  • crash工具使用 —— 省去输入struct或者union关键字
    使用crash工具根据地址查看结构体的内容时,有时需要输入struct或者union,多少有些繁琐,crash提供了*命令,直接跟在结构体或者联合体的名字的前面即可。示例:带structcrash>structkmem_cache-xffff893751f60800structkmem_cache{cpu_slab=0x5fc135c77b40,flags=......
  • 南方cass软件下载-南方cass7.0/9.0/11.0中文版 各个版本下载
    南方CASS9.1是款非常专业的cad图形设计软件,它是在cad的基础上二次开发的,cass必须配合cad软件才能够使用,功能非常强大,界面简洁明晰、操作方便快捷。南方CASS9.0能完美的兼容AutoCAD2000、AutoCAD2004、AutoCAD2005、AutoCAD2006以及AutoCAD2010等等的加载,全新的属性面板,更精准的查......
  • 南方CASS9.1下载-南方CASS官方最新版下载「cad图形」各个版本下载
    Cass9.0是其中一种遥感图像处理软件,提供了一系列的功能来处理和分析遥感数据。遥感图像处理的一个重要环节是图像准,即将多幅图像进行对齐、重叠,以实现后续的分析处理。控制点配准和变换处理是其中重要的一部分,下面将介绍如何在Cass9.0中进行这方面的处理。软件地址:看置顶贴南方CASS......
  • C/C++ const关键字 解读
    Thecollocationbetweenconstandoriginalpointerisconfusedtomanypeople.Therearetwousagesofit.Thefirstoneisavariablepointerthatpointsaconstantdata.i.e.constint*p#include<iostream>intmain(){ inta=1,b=2; const......
  • 05_其他关键字
    其他关键字const只读只能初始化,不能赋值,只读register寄存器如果变量被高频使用,会自动将变量存储在寄存器中,目的:提高访问效率如果用户想将变量直接放入寄存器中,可以使用register修饰寄存器变量volatile强制访问内存volatileintdata=0;//访问data强......
  • c语言-关键字static
    局部变量:运行周期=函数的运行周期全局变量:运行周期=整个程序的运行周期(程序可以是多个.c文件组成)static可以修饰:1、局部变量(函数内定义的)2、全局变量(函数外定义的) 3、函数1.修饰局部变量->静态局部变量:开辟存储空间。在编译的过程中,会在数据区为该变量开辟空间,并对其进行......