Java并发基石_CAS原理实战02_CAS实现原理

news2024/11/24 14:28:03

文章目录

    • 什么是CAS?
    • CAS的实现原理是什么?
    • cmpxchg指令怎么保证多核心下的线程安全?
    • 什么是ABA问题?
    • 如何解决ABA问题呢?

什么是CAS?

CAS,全称CompareAndSwap,比较并替换。
CAS包含了三个操作数,内存位置值V,期望值A,新值B,如果内存位置值V与期望值A匹配,处理器就将内存位置值更新为新值B,否则不做任何操作。无论发生哪种情况,它都会在CAS指令之前返回该位置的值。

sun.misc.Unsafe类中提供了对CAS操作的支持

/**
     * Atomically update Java variable to <tt>x</tt> if it is currently
     * holding <tt>expected</tt>.
     * @return <tt>true</tt> if successful
     */
    public final native boolean compareAndSwapObject(Object o, long offset,
                                                     Object expected,
                                                     Object x);

    /**
     * Atomically update Java variable to <tt>x</tt> if it is currently
     * holding <tt>expected</tt>.
     * @return <tt>true</tt> if successful
     */
    public final native boolean compareAndSwapInt(Object o, long offset,
                                                  int expected,
                                                  int x);

    /**
     * Atomically update Java variable to <tt>x</tt> if it is currently
     * holding <tt>expected</tt>.
     * @return <tt>true</tt> if successful
     */
    public final native boolean compareAndSwapLong(Object o, long offset,
                                                   long expected,
                                                   long x);
  • o:表示要操作的对象
  • offset:表示要操作对象中属性地址的偏移量
  • expected:表示期望值
  • x:表示新值

CAS的实现原理是什么?

CAS通过调用Java native interface的代码实现,允许Java调用其他语言,compareandswap系列的方法就是借助c语言调用cpu底层指令(cmpxchg)来实现的,cpu执行该指令时就实现了比较并替换的操作。

cmpxchg指令怎么保证多核心下的线程安全?

系统底层进行CAS操作的时候,首先会判断当前系统是否为多核,如果是就会对总线进行加锁,且只有一个线程可以可以对总线加锁成功,加锁成功之后就会执行CAS操作。

什么是ABA问题?

CAS操作需要在操作值的时候判断值有没有变化,如果没有变化则更新。但是如果一个值原来是A,在CAS方法执行之前,另一个线程先把A的值修改为B,然后又修改为A,那么CAS操作就会误认为A的值没有发生过变化,但是实际上A是发生过变化的。

代码模拟测试:

package juc.cas;

import java.util.concurrent.atomic.AtomicInteger;

public class ABADemo {
//    原子变量,是使用CAS实现的
    public static AtomicInteger a = new AtomicInteger(1);

    public static void main(String[] args) {
        Thread main = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("操作线程"+Thread.currentThread().getName()+",初始值"+a.get());
                try {
                    int expectNum = a.get();
                    int newNum = expectNum + 1;
                    Thread.sleep(1000);
                    boolean isCASSuccess = a.compareAndSet(expectNum,newNum);
                    System.out.println("操作线程"+Thread.currentThread().getName()+",CAS操作"+isCASSuccess);
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        },"主线程");

        Thread other = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
//                    确保main线程先执行
                    Thread.sleep(20);
                    a.incrementAndGet();
                    System.out.println("操作线程"+Thread.currentThread().getName()+",increment:"+a.get());
                    a.decrementAndGet();
                    System.out.println("操作线程"+Thread.currentThread().getName()+",decrement:"+a.get());
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        },"干扰线程");
        main.start();
        other.start();
    }
}

在这里插入图片描述
可以看到,CAS操作是成功了的。

如何解决ABA问题呢?

引入版本号机制,A每被修改一次版本号的值就会+1,当执行CAS操作的时候,拿到期望值的版本号与当前值的版本号进行比对,如果一致,则执行CAS操作。
在这里插入图片描述
图中的stamp就是版本号。

	 * @param expectedReference the expected value of the reference//期望引用
     * @param newReference the new value for the reference//新值引用
     * @param expectedStamp the expected value of the stamp//期望引用的版本号
     * @param newStamp the new value for the stamp//新值引用的版本号
     * @return {@code true} if successful
public boolean compareAndSet(V   expectedReference,
                                 V   newReference,
                                 int expectedStamp,
                                 int newStamp) {
        Pair<V> current = pair;
        return
            expectedReference == current.reference &&
            expectedStamp == current.stamp &&
            ((newReference == current.reference &&
              newStamp == current.stamp) ||
             casPair(current, Pair.of(newReference, newStamp)));
    }

代码模拟测试:

package juc.cas;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;

public class ABADemo {
//    原子变量,是使用CAS实现的
    public static AtomicStampedReference<Integer> a = new AtomicStampedReference(new Integer(1),1);

    public static void main(String[] args) {
        Thread main = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("操作线程"+Thread.currentThread().getName()+",初始值"+a.getReference());
                try {
                    Integer expectReference = a.getReference();
                    Integer newReference = expectReference + 1;
                    Integer expectStamp = a.getStamp();
                    Integer newStamp = expectStamp+1;
                    Thread.sleep(1000);
                    boolean isCASSuccess = a.compareAndSet(expectReference,newReference,expectStamp,newStamp);
                    System.out.println("操作线程"+Thread.currentThread().getName()+",CAS操作"+isCASSuccess);
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        },"主线程");

        Thread other = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
//                    确保main线程先执行
                    Thread.sleep(20);
                    a.compareAndSet(a.getReference(),a.getReference()+1,a.getStamp(),a.getStamp()+1);
                    System.out.println("操作线程"+Thread.currentThread().getName()+",increment:"+a.getReference());
                    a.compareAndSet(a.getReference(),a.getReference()-1,a.getStamp(),a.getStamp()-1);

                    System.out.println("操作线程"+Thread.currentThread().getName()+",decrement:"+a.getReference());
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        },"干扰线程");
        main.start();
        other.start();
    }
}

在这里插入图片描述
引入版本号之后,CAS就失败了。

在这里插入图片描述
文章参考:小刘老师讲源码

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

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

相关文章

MyBatis --- 缓存、逆向工程、分页插件

一、MyBatis的缓存 1.1、MyBatis的一级缓存 一级缓存是SqlSession级别的&#xff0c;通过同一个SqlSession查询的数据会被缓存&#xff0c;下次查询相同的数据&#xff0c;就会从缓存中直接获取&#xff0c;不会从数据库重新访问 使一级缓存失效的四种情况&#xff1a; 1、…

MySQL高级第十二篇:数据库事物概述和隔离级别

MySQL高级第十二篇&#xff1a;数据库事物概述和隔离级别一、数据库事物概述1. SHOW ENGINES 查看存储引擎2. 事物ACID特性原子性&#xff08;atomiity&#xff09;一致性&#xff08;consistency&#xff09;&#xff1a;隔离性&#xff08;isolation&#xff09;持久性&#…

使用java实现自动扫雷

写在前面 本项目已在github开源&#xff0c;链接https://github.com/QZero233/JavaAutoMinesweeper 本文的写作风格可能会有些奇怪&#xff0c;这是笔者的一次全新的尝试&#xff0c;后续会换回写blog的文风的 摘要 本文提出了一个全自动完成扫雷游戏的解决方案&#xff0c;…

【Kubernetes】 多云管理策略解析

文章目录Kubernetes 多云的实现1. 前言1.1 Kubernetes 多云的现实需求2. Kubernetes 多云的架构设计2.1 跨云 Kubernetes 的挑战2.1.1 不同云厂商的接口不兼容2.1.2 多云环境中的安全问题2.1.3 跨云环境中的网络问题2.2 Kubernetes 多云的架构设计2.2.1 统一网络管理2.2.2 使用…

新能源汽车高压配电管理(PDU/BDU)

一、概念与组成 PDU(Power Distribution Unit)&#xff0c;即高压配电单元&#xff0c;功能是负责新能源车高压系统中的电源分配与管理&#xff0c;为整车提供充放电控制、高压部件上电控制、电路过载短路保护、高压采样、低压控制等功能&#xff0c;保护和监控高压系统的运行…

MacOS系统启动React前端项目时报错Error: EMFILE: too many open files, open解决方法

错误场景 最近在开发React的前端微应用&#xff0c;启动时模块构建报错Module build failed&#xff0c; Error: EMFILE: too many open files, 如下图所示&#xff1a; Error: EMFILE: too many open files的错误&#xff0c;经排查是因为单个微应用项目较大&#xff0c;发…

【Linux安装数据库】Ubuntu安装mysql并连接navicat

Linux系统部署Django项目 文章目录Linux系统部署Django项目一、mysql安装二、mysql配置文件三、新建数据库和用户四、nivacat链接mysql一、mysql安装 linux安装mysql数据库有很多教程&#xff0c;根据安装方式不同&#xff0c;相关的步骤也不同。可以参考&#xff1a;【Linux安…

前端基础(HTML、CSS、JS、jQuery)

文章目录一、HTML基础1.1 常用标签&#xff08;表格、表单、按钮等&#xff09;1.2 其他一些标签&#xff08;书签、显示效果、缩写等&#xff09;二、CSS基础2.1 CSS引入方式2.2 CSS选择器2.3 CSS常用属性三、JavaScript3.1 JS使用方式3.2 变量和数据类型3.3 函数、作用域、条…

Unity基础框架从0到1(五)延时任务调度模块

索引 这是Unity基础框架从0到1的第五篇文章&#xff0c;前面的文章和对应的视频我一起列到这里&#xff1a; 文章 Unity基础框架从0到1 开篇 Unity游戏框架从0到1 (二) 单例模块 Unity基础框架从0到1&#xff08;三&#xff09;高效的全局消息系统 Unity基础框架从0到1&a…

CentOS 7 DNS服务器架设

CentOS 7 DNS服务器部署 项目背景和要求 要保证即能够解析内网域名linuxidc.local的解析&#xff0c;又能解析互联网的域名。 主DNS服务器&#xff1a;ZZYH1.LINUXIDC.LOCAL 辅助DNS服务器&#xff1a;ZZYH2.LINUXIDC.LOCAL 包含以下域的信息&#xff1a; 1、linuxidc.lo…

mybatis多表联查(一对一、一对多/多对一、多对多)

mybatis多表联查(一对一、一对多/多对一、多对多) 在开发过程中单表查询往往不能满足需求分析的很多功能&#xff0c;对于比较复杂业务来说&#xff0c;关联的表有几个&#xff0c;甚至是几十个并且表与表之间的关联相当复杂。为了能够实现复杂的功能业务&#xff0c;就必须进…

Java 并发工具合集 JUC 大爆发!!!

并发工具类 通常我们所说的并发包也就是 java.util.concurrent (JUC)&#xff0c;集中了 Java 并发的各种工具类&#xff0c; 合理地使用它们能帮忙我们快速地完成功能 。 1. CountDownLatch CountDownLatch 是一个同步计数器&#xff0c;初始化的时候 传入需要计数的线程等待数…

Monaco Editor编辑器教程(二七):集成多个GitLab编辑器颜色样式主题

前言 在开发编辑器时,未来满足开发者的审美需求,有时需要设计多套颜色主题,最基础的是黑色主题和白色主题。如果用户愿意出钱的话,可以加上一些其他花里胡哨的颜色主题,在vscode中是可以搜到。今天就来教大家一些,如何借助现成的资源来为自己的monaco编辑器增加丰富的颜…

JavaScript控制语句及搭建前端服务器

目录 一、for in 二、for of 三、try catch 四、搭建前端服务器 &#xff08;1&#xff09;安装nvm &#xff08;2&#xff09;检查npm &#xff08;3&#xff09;搭建前端服务器 一、for in 主要用来遍历对象 let father {name:张三, age:18, study:function(){}};f…

Linux常用的网络命令有哪些?快速入门!

在Linux系统中&#xff0c;有许多常用的网络命令可以用来进行网络配置和故障排除。这些命令可以帮助我们了解网络的状态和性能&#xff0c;并且可以快速诊断和解决网络问题。在本文中&#xff0c;我们将介绍一些常用的Linux网络命令&#xff0c;并提供一些案例来帮助您更好地理…

高数基础2

目录 函数的极限&#xff1a; 自变量趋向于有限值时函数的极限 左右极限 需要分左右极限的三种问题&#xff1a; 例题&#xff1a; 极限性质&#xff1a; 函数的保号性&#xff1a; 函数的保号性&#xff1a; 例题&#xff1a; 极限值与无穷小的关系 极限存在准则&#…

FRRoute 路由信息采集 + MPLS VPN隧道信息采集与识别

FRRoute 路由信息采集 MPLS VPN隧道信息采集与识别FRRoute数据库-表路由信息采集命令输出字段 -> 映射到 -> 数据库字段数据结构 算法show int brroute -nshow interfaces loMPLS VPN隧道信息采集与识别step 1 : 采集mpls邻居表step 2 : MPLS 隧道识别FRRoute 数据库-…

RK3568平台开发系列讲解(Linux系统篇)线程 pthread 详解

🚀返回专栏总目录 文章目录 一、POSIX 线程模型二、pthread_create()创建线程三、线程属性3.1、初始化线程对象属性3.2、销毁一个线程属性对象3.3、线程的分离状态3.4、线程的调度策略3.5、线程的优先级3.6、线程栈四、线程退出五、回收已终止线程的资源沉淀、分享、成长,让…

Word处理控件Aspose.Words功能演示:使用C#创建MS Word文档

Aspose.Words是一种高级Word文档处理API&#xff0c;用于执行各种文档管理和操作任务。API支持生成&#xff0c;修改&#xff0c;转换&#xff0c;呈现和打印文档&#xff0c;而无需在跨平台应用程序中直接使用Microsoft Word。 Aspose API支持流行文件格式处理&#xff0c;并…

苹果6信号不好的快速解决方法

许多朋友反馈&#xff0c;苹果6的信号不佳&#xff0c;建议从以下方面查找&#xff1a; 方法一&#xff1a;开启飞行模式后再关闭 有时候手机由于周围环境网络比较差&#xff0c;会导致信号处于无服务状态&#xff0c;这时后我们开启飞行模式后再关闭飞行模式&#xff0c;系统就…