java多线程操作之CAS

news2024/9/22 9:36:22

1,什么是CAS?

CAS(Compare-And-Swap) 比较并交换,用于实现同步和锁机制。经常配合juc中Atomic相关类进行。Atomic相关类无法解决aba问题。


2,CAS核心思想是什么?

比较和交换。
本质上就是乐观锁 + 自旋
这里需要注意的是CAS能保证原子性是介于使用CAS时,比较对象采用了Atomic相关类。如果没有,则不保证原子性。


3,CAS的基本概念

三个操作数
对象: 要比较和可能修改的对象
预期值:当前对象的预期
新值:如果与预期值相等,则更新为新值
两个步骤
比较:比较当前对象是否与预期相等
交换:如果当前对象与预期相等,则交换更新为新值


4,CAS锁的实现

CAS 锁是一种基于 CAS 操作实现的轻量级锁,通常用于实现自旋锁。它的主要优点是避免了传统锁机制的上下文切换开销,提高了并发性能。


4.1 基本实现

package com.rojer.threadCAS;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 一般情况都配合Atomic类保证其原子性
 */
public class CASExample {
    private AtomicInteger value = new AtomicInteger(0);

    /**
     * compareAndSet(CAS)方法的底层原理基于硬件支持的原子操作,
     * 通过 Unsafe 类封装实现。在现代处理器中,CAS 操作通常通过硬件指令(如 x86 架构上的 cmpxchg 指令)实现。
     * 这些硬件指令能够在不被其他线程打断的情况下完成读取、比较和写入操作,从而实现原子性。
     */
    public void increment() {
        int oldValue, newValue;
        do {
            oldValue = value.get();
            newValue = oldValue + 1;
        } while (!value.compareAndSet(oldValue, newValue));
    }

    public int getValue() {
        return value.get();
    }

    /**
     * 错误的示例
     */
    private Integer value1 = 0;

    public void increment1() {
        // value1++无法保证原子性操作
        value1++;
    }

    public int getValue1() {
        return value1;
    }

    public static void main(String[] args) {
        CASExample example = new CASExample();
        for (int i = 0; i < 1000; i++) {
            new Thread(example::increment).start();
        }
        // 由于多线程的原因,可能需要等待所有线程执行完
        // 这里简单等待
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("AtomicInteger 验证CAS,值为:" + example.getValue());

        for (int i = 0; i < 1000; i++) {
            new Thread(example::increment1).start();
        }

        // 由于多线程的原因,可能需要等待所有线程执行完
        // 这里简单等待
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Integer 验证CAS,值为:" + example.getValue1());
    }
}

执行测试

4.2 当是自定义object为比较对象时

package com.rojer.threadCAS;

import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * 测试CAS比较为一个对象时,避免ABA问题
 */
public class CustomObjectCASExample {

    // 创建测试静态内部类
    private static class CustomObject {
        private final int id;

        public CustomObject(int id) {
            this.id = id;
        }

        public int getId() {
            return id;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            CustomObject that = (CustomObject) o;
            return id == that.id;
        }

        @Override
        public int hashCode() {
            return Integer.hashCode(id);
        }
    }

    // 对象保证原子性使用AtomicStampedReference, 这里第二个参数就是类似版本号,防止ABA问题产生
    private final AtomicStampedReference<CustomObject> reference =
            new AtomicStampedReference<>(new CustomObject(0), 0);

    public void update(int newId) {
        CustomObject oldObject, newObject;
        int oldStamp, newStamp;
        do {
            oldObject = reference.getReference();
            oldStamp = reference.getStamp();
            newObject = new CustomObject(newId);
            newStamp = oldStamp + 1;
        } while (!reference.compareAndSet(oldObject, newObject, oldStamp, newStamp));
    }

    public CustomObject getObject() {
        return reference.getReference();
    }

    public static void main(String[] args) {
        CustomObjectCASExample example = new CustomObjectCASExample();
        for (int i = 0; i < 1000; i++) {
            final int id = i;
            new Thread(() -> example.update(id)).start();
        }
        // 由于多线程的原因,可能需要等待所有线程执行完
        // 这里简单等待
        try { Thread.sleep(1000); } catch (InterruptedException e) { }
        System.out.println(example.getObject().getId());
    }

}

4.3可重入锁的情况 -- 这里已经不是CAS的逻辑了。做额外展示(没有自旋和比较情况下,如何保证原子性)

package com.rojer.threadCAS;

import java.util.concurrent.locks.ReentrantLock;

/**
 * CAS之使用可重入锁
 * 使用可重入锁之后,就不需要自选比较,因为可重入锁也是独占锁
 */
public class ReentrantLockExample {
    private int value = 0;
    private final ReentrantLock lock = new ReentrantLock();

    public void increment() {
        lock.lock();
        try {
            value++;
        } finally {
            lock.unlock();
        }
    }

    public int getValue() {
        return value;
    }

    public static void main(String[] args) {
        ReentrantLockExample example = new ReentrantLockExample();
        for (int i = 0; i < 100; i++) {
            new Thread(example::increment).start();
        }
        // 由于多线程的原因,可能需要等待所有线程执行完
        // 这里简单等待
        try { Thread.sleep(1000); } catch (InterruptedException e) { }
        System.out.println(example.getValue());
    }
}

5, 优点和缺点

优点:
低延迟: 避免传统锁的上下文切换开销
无锁:减少了线程阻塞和唤醒的开销

缺点:
自旋开销: 在高并发场景自旋开销可能过大
适用性: 不适合持有锁的操作,长时间自旋会影响系统性能。
可能导致ABA问题

6,展开1--什么是ABA问题?

1,线程 A 执行 CAS 操作,检查一个值(假设为 A)并希望将其更新为另一个值(假设为 B)。
2, 在 A 线程执行 CAS 操作期间,另一个线程 B 将值从 A 更新为另一个值 C,然后又将其更新回 A。
3, 当线程 A 再次执行 CAS 操作时,它仍然看到值为 A,并认为数据没有被改变,因此成功地将值更新为 B,但实际上,数据在 A 和 B 之间发生了变化。
ABA 问题 的关键在于 CAS 操作无法区分值是否在检查期间被修改和恢复,导致线程 A 在看似安全的操作下可能会做出错误的决策。

7,展开2--为什么Atomic类能保证原子性

atomic类方法底层都采用了Unsafe 类的方法。

Unsafe 类提供了直接操作内存和硬件级别原子操作的方法。compareAndSwapIntUnsafe 类中的一个本地方法(native),通过 JNI 调用底层硬件指令实现原子操作。

  • obj:需要操作的对象。
  • offset:对象中字段的内存偏移量。
  • expected:期望值。
  • x:更新值。

8,展开3--JUC中使用到CAS的地方以ConcurrentHashMap为例

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

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

相关文章

数字电路-建立时间和保持时间详解

对于数字系统而言&#xff0c;建立时间&#xff08;setup time&#xff09;和保持时间&#xff08;hold time&#xff09;是数字电路时序的基础。数字电路系统的稳定性&#xff0c;基本取决于时序是否满足建立时间和保持时间。我自己在初学时一度很难理解清楚他们的概念&#x…

android studio开发

Kotlin 编程简介 | Android Basics Compose - First Android app | Android Developers (google.cn) 这是官网的教程&#xff0c;实现试一下。 之后进入课程 您的第一个 Kotlin 程序 (google.cn) 程序可以被视为一系列指示计算机或设备执行某项操作的指令&#xff0c;

Highlight.js示例

图例 代码在图片后面 点赞❤️关注&#x1f64f;收藏⭐️ 源代码 <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"…

2024007月份 制作一个 Windows 10 U disk 安装工具

1&#xff0c;下载微软官方 Win10 U盘安装工具 工具名称&#xff1a; MediaCreationTool 下载地址&#xff1a; https://www.microsoft.com/zh-cn/software-download/windows10 2&#xff0c;制作 U盘安装盘 双击打开&#xff0c;并单击“接受” 选中 为另一台电脑创建安…

微信小程序如何实现登陆和注册功能?

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

python中的os模块和shutil模块

目录 os 1. 获取当前脚本绝对路径 2.获得工作路径&#xff1b; 3.该路径文件和目录 4.walk&#xff0c;查看目录下所有的文件&#xff08;含子孙文件&#xff09; 5.创建文件夹 6.os.makedirs(path) 7.路径拼接 8. 获取当前文件的上级目录 9.判断路径是否存在 10.是…

linux系统查看父子进程

① 查找特定进程的父进程 ps -o pid,ppid,cmd -p 1234 查找进程 PID 为 1234 的父进程 ② 显示所有进程的树状结构 pstree ③ 显示特定进程及其父进程的树状结构 pstree -s 1234 ④ 启动 top 后&#xff0c;按下 c 键可以查看完整命令&#xff0c;按下 f 键进入字段管理界面…

Java | Leetcode Java题解之第233题数字1的个数

题目&#xff1a; 题解&#xff1a; class Solution {public int countDigitOne(int n) {// mulk 表示 10^k// 在下面的代码中&#xff0c;可以发现 k 并没有被直接使用到&#xff08;都是使用 10^k&#xff09;// 但为了让代码看起来更加直观&#xff0c;这里保留了 klong mu…

载波相位定位原理

在现代定位系统中&#xff0c;载波相位测距技术因其高精度而备受青睐。本文将探讨其工作原理&#xff0c;以及如何通过数学模型和算法来校正测量中的误差。 载波相位测距模型 载波相位测距是基于接收卫星发射的载波信号相位变化来进行距离测量的技术。它利用了信号传输过程中…

springboot系列教程(二):Log4j2日志信息(含源码)

一、Log4j2日志简介 日志打印是了解Web项目运行的最直接方式&#xff0c;所以在项目开发中是需要首先搭建好的环境。 1、Log4j2特点 核心特点 相比与其他的日志系统&#xff0c;log4j2丢数据这种情况少&#xff1b;disruptor技术&#xff0c;在多线程环境下&#xff0c;性能…

攻防世界 level3

这道题把附件下载下来发现一个libc(动态链接库)&#xff0c;那这道题估计需要利用libc来确定elf中函数的地址 国际惯例checksec&#xff0c;发现level3没开栈溢出保护和地址随机化&#xff0c;libc全开 拖入32位ida&#xff0c;没发现留后门和system函数&#xff0c;只有个writ…

SCI一区级 | Matlab实现GJO-CNN-LSTM-Multihead-Attention多变量时间序列预测

SCI一区级 | Matlab实现GJO-CNN-LSTM-Mutilhead-Attention多变量时间序列预测 目录 SCI一区级 | Matlab实现GJO-CNN-LSTM-Mutilhead-Attention多变量时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现GJO-CNN-LSTM-Mutilhead-Attention金豺优化算…

OrangePi AIpro在安防领域的深思和实战(旷视科技CNN模型ShuffleNetV1开发案例测试)

一、前言 公司最近有个项目是安防领域的&#xff0c;主要用在边缘结点&#xff0c;虽然已做成形&#xff0c;但是还是存在一些缺陷&#xff0c;例如&#xff1a;算力问题&#xff0c;开发板的成熟问题&#xff0c;已经各种技术的解决方案落地问题。目前我们集成了很多功能&…

在家上网IP地址是固定的吗?

在数字化时代&#xff0c;互联网已成为我们日常生活中不可或缺的一部分。无论是工作、学习还是娱乐&#xff0c;我们都离不开网络的支持。然而&#xff0c;当我们在家中接入互联网时&#xff0c;可能会产生这样一个疑问&#xff1a;在家上网IP地址是固定的吗&#xff1f;下面一…

春招冲刺百题计划|双指针

Java基础复习 Java数组的声明与初始化Java ArrayListJava HashMapJava String 类Java LinkedListJava Deque继承LinkedListJava SetJava 队列优先队列:第二题用到了Java数组划分Java数组转ArrayListString 转数字String 这一部分&#xff0c;代码随想录写得超级好&#xff01…

哪些场景下适合使用人工智能作词软件来写歌词

以下是一些适合使用人工智能作词软件的场景&#xff1a; 软件我们选用“妙笔生词”智能写歌词软件&#xff08;veve299&#xff09;来操作。 1.创作灵感枯竭时&#xff1a;当创作者陷入思维困境&#xff0c;找不到新的创意和方向&#xff0c;人工智能作词软件可以快速提供一些…

Learning vtkjs之hello vtk

学习vtkjs 最近由于工作需要&#xff0c;开始学习vtkjs的相关内容&#xff0c;发现其实在医疗和工业领域&#xff0c;这个vtk的库的example还是非常有帮助&#xff0c;但是实际用的一些开发工具&#xff0c;或者研发生态却没有three的好&#xff0c;也就是能抄写的东西不多&am…

PanTools v1.0.27 多网盘批量管理、遍历分享、转存、重命名、复制...

一款针对多个热门网盘的文件管理、批量分享、批量转存、批量重命名、批量复制、批量链接检测、跨账号移动文件、多账号文件搜索等&#xff0c;支持不同网盘的不同账号的资源文件操作。适用于网站站长、资源爱好者、网盘拉新等&#xff0c;对于管理名下具有多个网盘多个账号具有…

昇思25天学习打卡营第20天|CycleGAN图像风格迁移互换

模型简介 CycleGAN(Cycle Generative Adversarial Network) 即循环对抗生成网络&#xff0c;来自论文 Unpaired Image-to-Image Translation using Cycle-Consistent Adversarial Networks 。该模型实现了一种在没有配对示例的情况下学习将图像从源域 X 转换到目标域 Y 的方法。…

3D问界-深入理解骨骼绑定与蒙皮绑定在三维动画中的角色

问题提出&#xff1a;什么是蒙皮绑定&#xff0c;什么是骨骼绑定 蒙皮绑定&#xff08;Skinning&#xff09;和骨骼绑定&#xff08;Bone Binding&#xff09;通常是在计算机图形学和动画制作中使用的术语&#xff0c;用来描述将模型或角色的几何形状与骨骼系统相关联的过程。这…