【多线程】CAS的应用 | CAS的概念 | 实现原子类 | 实现自旋锁

news2024/11/15 13:21:30

文章目录

    • 一、CAS
          • 1.什么是CAS
          • 2.实现原子类
          • 3.实现自旋锁


一、CAS

1.什么是CAS

Compare and swap 比较并交换。

比较交换的是 内存 和 寄存器

比如此时有一个内存 : M。 还有两个寄存器A,B

​ CAS ( M , A , B ) :如果M和A的值相同的话,就把M和B的值进行交换(交换的目的是为了把B赋值给M,M = B),同时整个操作返回 true。如果M和A的值不同的话,无事发生,同时整个操作返回false。

//伪代码:
boolean CAS(address, expectValue, swapValue) {
 if (&address == expectedValue) {
   &address = swapValue;
        return true;
   }
    return false;
}
  • CAS其实是一个CPU指令,一个CPU指令,就可以完成上述比较交换的逻辑。
  • 单个的CPU指令是原子的。所以可以使用CAS来完成一些操作来代替“加锁”。为编写线程安全的代码提供了线的思路,基于CAS实现线程安全的方式,也叫“无锁编程”。

优点:保证线程安全,同时避免发生阻塞。

缺点:代码可读性差,并且只能适用在特定场景,不如加锁普适。

​ CAS本质上是CPU提供的指令,被操作系统封装后,提供成为APi。又再次被JVM进行封装,提供成API,最终由程序员调用方法实现操作。

2.实现原子类

之前int count++的操作,就不是原子的(load,add,save)

在Java标准库中,提供了原子类,例如AtomicInteger,基于CAS的方式对int进行封装,++就封装成原子操作了。

    public static AtomicInteger count = new AtomicInteger(0);
    //对int进行原子化包装
    //0作为构造方法的参数
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            for (int i = 0; i < 50000; i++) {
               count.getAndIncrement();//count++; 后置++
//                count.incrementAndGet();//++count  前置++
//                count.getAndDecrement();//count--; 后置--;
//                count.decrementAndGet();//--count 前置--
            }
        });
        Thread t2 = new Thread(()->{
            for (int i = 0; i < 50000; i++) {
                count.getAndIncrement();
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(count);
    }

在Java中,有些操作是偏底层的操作,在使用时有着更多的注意事项,稍有不慎就容易写出问题。这些操作就同一放在unsafe这个类中

在这里插入图片描述

//伪代码
class AtomicInteger {
    private int value;//相当于内存
    public int getAndIncrement() {
        int oldValue = value;
        //oldValue相当于寄存器    
        while ( CAS(value, oldValue, oldValue+1) != true) {
                  //CAS(内存,寄存器A,寄存器B)
            oldValue = value;
       }
        return oldValue;
   }
}

寄存器A先保存当前内存的值,CAS来判断此时有没有被穿插执行。如果没有穿插执行,内存的值等于寄存器A的值,把寄存器B(B执行+1操作)的值赋值给内存,同时CAS返回的是true,循环不成立,返回是是自增前的值。

如果被穿插执行了,CAS中,内存的值不等于寄存器A的值,CAS直接返回false.此时循环成立,将被穿插执行过的内存值,重新赋值给寄存器A,也就是说要再读一遍内存。再次执行CAS。

线程不安全的本质是进行操作的过程中,穿插执行。

1.加锁是通过阻塞的方式,避免穿插。

2.CAS是通过重试的方式,避免穿插。如果值和之前不相等了,重新更新一下,在进行操作。

3.实现自旋锁
//伪代码:
public class SpinLock {
    private Thread owner = null;//记录当前这个锁被哪个线程获取到。 null表示未加锁
    public void lock(){//加锁
        // 通过 CAS 看当前锁是否被某个线程持有. 
        // 如果这个锁已经被别的线程持有, 那么就自旋等待. 
        // 如果这个锁没有被别的线程持有, 那么就把 owner 设为当前尝试加锁的线程. 
        while(!CAS(this.owner, null, Thread.currentThread())){
       }
   }
    public void unlock (){//解锁
        this.owner = null;
   }
}
  • while循环中,CAS判断当前owner是不是未加锁状态。如果是未加锁的,就把owner赋值成 Thread.currentThread())(调用lock方法的线程引用)进行加锁。如果已经被加锁了,CAS返回false,取反之后,while就会反复执行。一旦其他线程释放了这个锁,owner变成null时,因为while不停循环,就会立即拿到当前这把锁。

缺点:while循环在忙等的过程中,不断消耗CPU资源。如果加锁的概率不大,付出的CPU资源就是值得的。如果当前的锁冲突很激烈,可能会拿不到,此时自旋锁就不适用了。

点击移步博客主页,欢迎光临~

偷cyk的图

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

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

相关文章

UI设计/交互设计/视觉设计项目汇报/作品集Figma/PPT模板

作为UI设计/交互设计/视觉设计师&#xff0c;创建作品集对于向潜在客户或雇主展示您的技能、创造力和风格至关重要。以下分步指南可帮助您创建令人印象深刻的作品集&#xff1a; 选择您的最佳作品&#xff1a;选择您最强大且最相关的设计项目&#xff0c;将其纳入您的作品集。…

Pytorch入门实战: 06-VGG-16算法-Pytorch实现人脸识别

第P6周&#xff1a;VGG-16算法-Pytorch实现人脸识别 &#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客 &#x1f356; 原作者&#xff1a;K同学啊 &#x1f3e1; 我的环境&#xff1a; 语言环境&#xff1a;Python3.8 编译器&#xff1a;Jupyter La…

微信小程序vue.js+uniapp服装商城销售管理系统nodejs-java

本技术是java平台的开源应用框架&#xff0c;其目的是简化Sping的初始搭建和开发过程。默认配置了很多框架的使用方式&#xff0c;自动加载Jar包&#xff0c;为了让用户尽可能快的跑起来spring应用程序。 SpinrgBoot的主要优点有&#xff1a; 1、为所有spring开发提供了一个更快…

python3--lxml pytoml.core.TomlError expected_equals报错解决

文章目录 一、问题二. 解决方法&#xff1a;三. 参考&#xff1a;四. 总结 一、问题 在ubuntu的armbian上的python3中安装lxml时报错了 安装命令是 pip3 install lxml报错简略信息如下图 File "/usr/share/python-wheels/pytoml-0.1.2-py2.py3-none-any.whl/pytoml/par…

iptables命令简介

正文共&#xff1a;3456 字 22 图&#xff0c;预估阅读时间&#xff1a;3 分钟 iptables/ip6tables命令&#xff0c;用于在Linux内核中设置、维护和检查IPv4和IPv6数据包过滤规则的表&#xff0c;从而实现IPv4/IPv6数据包过滤和NAT的管理工具。它可以定义多个不同的表&#xff…

CH341A/B USB转USART/I2C/SPI介绍

CH341A/B USB转USART/I2C/SPI介绍 &#x1f4cd;CH341官方文档&#xff1a;https://www.wch.cn/downloads/CH341DS2_PDF.html CH341A/B是一个USB总线的转接芯片&#xff0c;通过USB总线提供异步串口、打印口、并口以及常用的2线和4线等同步串行接口。 &#x1f341;芯片封装&a…

基于Springboot的社区疫情返乡管控系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的社区疫情返乡管控系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系…

uni-app为图片添加自定义水印(升级版)

前置内容 uni-app为图片添加自定义水印&#xff08;解决生成图片不全问题&#xff09; UI 升级 现在水印样式变成这样了&#xff1a; 代码 <template><canvas v-if"waterMarkParams.display" canvas-id"waterMarkCanvas" :style"canv…

【001_IoT/物联网通信协议基础: HTTP、Websocket、MQTT、AMQP、COAP、LWM2M一文搞懂】

001_IoT/物联网通信协议基础: HTTP、Websocket、MQTT、AMQP、COAP、LWM2M一文搞懂 文章目录 001_IoT/物联网通信协议基础: HTTP、Websocket、MQTT、AMQP、COAP、LWM2M一文搞懂创作背景通信模型ISO/OSI七层模型 和 TCP/IP四层模型网络通信数据包格式&#xff08;Ethernet II&…

Swift-20-基础数据类型

数据定义 语法规则 先来看下下面的代码 import Cocoavar num1 "four" //a var num2: String "four" //b var num3 4 //c var num4: Int 4 //d上面的几行代码都能正常运行&#xff0c;其中a和b行等价&#xff0c;c和d行等价。区另就在于是否声…

docker-compose 安装MongoDB续:创建用户及赋权

文章目录 1. 问题描述2. 分析2.1 admin2.2 config2.3 local 3. 如何连接3.解决 1. 问题描述 在这一篇使用docker-compose创建MongoDB环境的笔记里&#xff0c;我们创建了数据库&#xff0c;但是似乎没有办法使用如Robo 3T这样的工具去连接数据库。连接的时候会返回这样的错误&…

关系抽取与属性补全

文章目录 实体关系抽取的任务定义机器学习框架属性补全 实体关系抽取的任务定义 从文本中抽取出两个或者多个实体之间的语义关系&#xff1b;从文本获取知识图谱三元组的主要技术手段&#xff0c;通常被用于知识图谱的补全。美丽的西湖坐落于浙江省的省会城市杭州的西南面。&am…

IDEA中SVN 的使用

文章目录 前言一、svn安装二、IDEA集成SVN总结 前言 svn可以老牌的代码仓库了 说实话svn还是和git无法相比的,毕竟git有本地仓库的概念,可以很好的处理冲突,然而svn是没有本地仓库的概念的,所以只能拉取别人的代码,然后处理冲突后,才能提交代码; 由于最近的工作换成了用svn仓…

el-menu 有一级二级三级菜单

效果如下 菜单代码如下 <el-menu:default-active"menuDefaultActive"class"el-menu-box":text-color"menuTextColor":active-text-color"menuActiveTextColor":unique-opened"true"><!-- 一级菜单 --><tem…

线程池的核心参数有哪些???

线程池的核心参数包括以下七个&#xff1a; corePoolSize&#xff1a; 这是线程池中的核心线程数&#xff0c;即池中会保留的最少线程数。当提交任务时&#xff0c;如果当前线程数小于核心线程数&#xff0c;线程池会创建新的线程来执行任务。如果当前线程数等于或大于核心线程…

Docker - 简介

原文地址&#xff0c;使用效果更佳&#xff01; Docker - 简介 | CoderMast编程桅杆https://www.codermast.com/dev-tools/docker/docker-introduce.html Docker是什么&#xff1f; Docker 是一个开源的应用容器引擎&#xff0c;基于 Go 语言 并遵从 Apache2.0 协议开源。 D…

css-Echarts图表初始显示异常非完全显示

1.echarts图表初始加载异常 2.问题原因 初次加载时&#xff0c;由于外层使用%比 echarts dom元素没有完全加载完成&#xff0c;canvas绘画继承本身宽高&#xff0c;造成Echarts图表初始显示异常非完全显示。 3.使用echarts图表可参考以下代码&#xff08;实现一定的自适应&am…

stm32开发之threadx+emwin+awizard使用记录

前言 图形化开发界面选择(awizard)emwin使用的版本是6.10芯片采用的是stm32f407zgt6这里使用的开发板是普中麒麟f4系列的 lcd驱动文件&#xff08;基于提供的源码修改&#xff09; 1、这里是剔除了很多兼容其他显示屏部分的代码&#xff0c;只保留具体信号的代码,把一些全局…

Java实现AVL树

AVI树 如果一颗二叉搜索树不平衡,那么搜索效率会受影响 二叉搜索树如果不是这种不平衡的情况,时间复杂度可以达到O(logn) 但是像图中的这种不平衡情况时间复杂度为O(n),那么如何解决呢? 可以通过旋转解决 旋转之后并不会破坏二叉搜索树的特性 判断是否平衡有一个规则:如果一…

如何进行景气分析

景气分析是一种短期经济分析方法。主要分析短时间内&#xff08;一般指一年内&#xff0c; 或几个月内&#xff09;经济运行的态势&#xff0c;包括当前的状态和未来的趋势。景气分析可以为宏观经济政策提供重要的决策与参考信息&#xff0c;例如根据经济运行的方向、强弱可建议…