程序员深夜惨遭老婆鄙视,原因竟是CAS原理太简单?| 每一张图都力求精美

单一登录踢人效果 项目中实际应用过,摘出共享,以留备用. 立即下载

夜黑风高的晚上,一名苦逼程序员正在疯狂敲着键盘,突然他老婆带着一副睡眼朦胧的眼神瞟了下电脑桌面。于是有了如下对话:

老婆:这画的图是啥意思,怎么还有三角形,四边形?

我:我在画CAS的原理,要不我跟你讲一遍?

老婆:好呀!

请开始你的表演

案例:甲看见一个三角形积木,觉得不好看,想替换成五边形,但是乙想把积木替换成四边形。(前提条件,只能被替换一次)

案例

甲比较鸡贼,想到了一个办法:“我把积木带到另外一个房间里面去替换,并上锁,就不会被别人打扰了。”(这里用到了排他锁synchronized)

乙觉得甲太不厚道:“房间上了锁,我进不去,我也看不见积木长啥样。(因上了锁,所以不能访问)”

甲把房间锁住了

于是甲、乙想到了另外一个办法:谁先抢到积木,谁先替换,如果积木形状变了,则不允许其他人再次替换。(比较并替换CAS)

于是他们就开始抢三角形积木:

  • 场景1:甲抢到,替换成五边形,乙不能替换

    甲先抢到,替换成五边形
    • 乙后抢到,积木已经变为五边形了,乙就没机会替换了(因为甲、乙共一次替换机会)。

      乙不能替换
    • 假如甲先抢到了,积木还是三角形的,就把三角形替换成五边形了。

  • 场景2:乙抢到未替换,甲替换成功

    乙抢到未替换,甲替换成功
    • 假如乙先抢到了,但是突然觉得三角形也挺好看的,没有替换,放下积木就走开了。

    • 然后甲抢到了积木,积木还是三角形的,想到乙没有替换,就把三角形替换成五边形了。

  • 场景3:乙抢到,替换成三角形,甲替换成五边形,ABA问题

    • 假如乙先抢到了,但是觉得这个三角形是旧的,就换了另外一个一摸一样的三角形,只是积木比较新。

    • 然后甲抢到了积木,积木还是三角形的,想到乙没有替换,就把三角形替换成五边形了。

乙抢到,替换成三角形,甲替换成五边形,ABA问题

老婆听完后,觉得这三种场景都太简单了,原来计算机这么简单,早知道我也去学计算机。。。

mark

被无情鄙视了,好在老婆居然听懂了,不知道大家听懂没?

回归正传,我们用计算机术语来讲下Java CAS的原理

一、Java CAS简介

CAS的全称:Compare-And-Swap(比较并交换)。比较变量的现在值与之前的值是否一致,若一致则替换,否则不替换。

CAS的作用:原子性更新变量值,保证线程安全。

CAS指令:需要有三个操作数,变量的当前值(V),旧的预期值(A),准备设置的新值(B)。

CAS指令执行条件:当且仅当V=A时,处理器才会设置V=B,否则不执行更新。

CAS的返回值:V的之前值。

CAS处理过程:原子操作,执行期间不会被其他线程中断,线程安全。

CAS并发原语:体现在Java语言中sun.misc.Unsafe类的各个方法。调用UnSafe类中的CAS方法,JVM会帮我们实现出CAS汇编指令,这是一种完全依赖于硬件的功能,通过它实现了原子操作。由于CAS是一种系统原语,原语属于操作系统用于范畴,是由若干条指令组成,用于完成某个功能的一个过程,并且原语的执行必须是连续的,在执行过程中不允许被中断,所以CAS是一条CPU的原子指令,不会造成所谓的数据不一致的问题,所以CAS是线程安全的。

二、能写几行代码说明下吗?

在上篇讲volatile时,讲到了如何使用原子整型类AtomicInteger来解决volatile的非原子性问题,保证多个线程执行num++的操作,最终执行的结果与单线程一致,输出结果为20000。

这次我们还是用AtomicInteger。

首先定义atomicInteger变量的初始值等于10,主内存中的值设置为10

AtomicInteger atomicInteger = new AtomicInteger(10);

然后调用atomicInteger的CAS方法,先比较当前变量atomicInteger的值是否是10,如果是,则将变量的值设置为20

atomicInteger.compareAndSet(10, 20);

设置成功,atomicInteger更新为20

当我们再次调用atomicInteger的CAS方法,先比较当前变量atomicInteger的值是否是10,如果是,则将变量的值设置为30

atomicInteger.compareAndSet(10, 30);

设置失败,因atomicInteger的当前值为20,而比较值是10,所以比较后,不相等,故不能进行更新。

完整代码如下:

package com.jackson0714.passjava.threads;
import java.util.concurrent.atomic.AtomicInteger;
/**
 演示CAS compareAndSet 比较并交换
 * @author: 悟空聊架构
 * @create: 2020-08-17
 */
public class CASDemo {
    public static void  main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(10);
        Boolean result1 = atomicInteger.compareAndSet(10,20);
        System.out.printf("当前atomicInteger变量的值:%d 比较结果%s\r\n", atomicInteger.get(), result1);
        Boolean result2 = atomicInteger.compareAndSet(10,30);
        System.out.printf("当前atomicInteger变量的值:%d, 比较结果%s\n" , atomicInteger.get(), result2);
    }
}

执行结果如下:

当前atomicInteger变量的值:20 比较结果true
当前atomicInteger变量的值:20, 比较结果false
atomicInteger比较并交换的示例结果

我们来对比看下原理图理解下上面代码的过程

  • 第一步:线程1和线程2都有主内存中变量的拷贝,值都等于10

mark
  • 第二步:线程1想要将值更新为20,先要将工作内存中的变量值与主内存中的变量进行比较,值都等于10,所以可以将主内存中的值替换成20

mark
  • 第三步:线程1将主内存中的值替换成20,并将线程1中的工作内存中的副本更新为20

mark
  • 第四步:线程2想要将变量更新为30,先要将线程2的工作内存中的值与主内存进行比较10不等于20,所以不能更新

mark
  • 第五步:线程2将工作内存的副本更新为与主内存一致:20

mark

图画得非常棒!

mark

上述的场景和我们用Git代码管理工具是一样的,如果有人先提交了代码到develop分支,另外一个人想要改这个地方的代码,就得先pull develop分支,以免提交时提示冲突。

三、能讲下CAS底层原理吗?

源码调试

这里我们用atomicInteger的getAndIncrement()方法来讲解,这个方法里面涉及到了比较并替换的原理。

示例如下:

public static void  main(String[] args) throws InterruptedException {
    AtomicInteger atomicInteger = new AtomicInteger(10);
    Thread.sleep(100);

    new Thread(() -> {
        atomicInteger.getAndIncrement();
    }, "aaa").start();

    atomicInteger.getAndIncrement();
}
  • (1)首先需要开启IDEA的多线程调试模式

  • (2)我们先打断点到17行,main线程执行到此行,子线程aaa还未执行自增操作。

调试代码

getAndIncrement方法会调用unsafe的getAndAddInt方法,

public final int getAndIncrement() {
    return unsafe.getAndAddInt(this, valueOffset, 1);
}
  • (3)在源码getAndAddInt方法的361行打上断点,main线程先执行到361行

    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;
    }
    

    源码解释: 划重点!!!

    • var1:当前对象,我们定义的atomicInteger

    • var2:当前对象的内存偏移量

    • var4:当前自增多少,默认为1,且不可设为其他值

    • var5:当前变量的值

    • this.getIntVolatile(var1, var2):根据当前对象var1和对象的内存偏移量var2得到主内存中变量的值,赋值给var5,并在main线程的工作内存中存放一份var5的副本

1
  • (4)在362行打上断点,main线程继续执行一步

    2
    • var5获取到主内存中的值为10

  • (5)切换到子线程aaa,还是在361行断点处,还未获取主内存的值

    3
  • (6)子线程aaa继续执行一步,获取到var5的值等于10

4

(7)切换到main线程,进行比较并替换

this.compareAndSwapInt(var1, var2, var5, var5 + var4)

var5=10,通过var1和var2获取到的值也是10,因为没有其他线程修改变量。compareAndSwapInt的源码我们后面再说。

所以比较后,发现变量没被其他线程修改,可以进行替换,替换值为var5+var4=11,变量值替换后为 11,也就是自增1。这行代码执行结果返回true(自增成功),退出do while循环。return值为变量更新前的值10。

5

(8)切换到子线程aaa,进行比较并自增

因为此时aaa线程的var5=10,而主内存中的值已经更新为11了,所以比较后发现被其他线程修改了,不能进行替换,返回false,继续执行do while循环。

6
  • (9)子线程aaa继续执行,重新获取到的var=11

7
  • (10)子线程aaa继续执行,进行比较和替换,结果为true

    因var5=11,主内存中的变量值也等于11,所以比较后相等,可以进行替换,替换值为var5+var4,结果为12,也就是自增1。退出循环,返回变量更新前的值var5=11。

8

至此,getAndIncrement方法的整个原子自增的逻辑就debug完了。所以可以得出结论:

先比较线程中的副本是否与主内存相等,相等则可以进行自增,并返回副本的值,若其他线程修改了主内存中的值,当前线程不能进行自增,需要重新获取主内存的值,然后再次判断是否与主内存中的值是否相等,以此往复。

四、CAS有什么问题?

不知道大家发现没,aaa线程可能会出现循环多次的问题,因为其他线程可能将主内存的值又改了,但是aaa线程拿到的还是老的数据,就会出现再循环一次,就会给CPU带来性能开销。这个就是自旋。

  • 频繁出现自旋,循环时间长,开销大(因为执行的是do while,如果比较不成功一直在循环,最差的情况,就是某个线程一直取到的值和预期值都不一样,这样就会无限循环)

  • 只能保证一个共享变量的原子操作

    • 当对一个共享变量执行操作时,我们可以通过循环CAS的方式来保证原子操作

    • 但是对于多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候只能用锁来保证原子性

  • 引出来ABA问题(有彩蛋)

五、小结

本篇从和老婆的对话开始,以通俗的语言给老婆讲了CAS原理,其中还涉及到了并发锁。然后从底层代码一步一步debug,深入理解了CAS的原理。

每一张图都力求精美!分享+在看啊,大佬们!

手把手教你用Docker快速部署ZLMediaKit流媒体服务器(含WebRTC配置) 本文详细介绍了如何使用Docker快速部署ZLMediaKit流媒体服务器,并配置WebRTC功能。通过容器化部署,实现环境一致性、资源隔离和快速扩展,同时提供WebRTC专项配置、多协议推拉流实战测试及生产环境优化建议,帮助开发者高效搭建企业级流媒体平台。 阅读详情

相关推荐

告别社区5级:一个脚本搞定小米/红米BL解锁的账号绑定限制(附资源下载与避坑指南)

本文提供了一种绕过小米/红米设备Bootloader解锁社区等级限制的技术方案,适用于MIUI 12.5-14和HyperOS 1.0系统。通过自动化脚本临时修改系统组件,用户可快速完成账号绑定和设备解锁流程,附详细操作指南和常见问题解决方案。

weixin_27330481的博客 737

Cas实现子系统登录互踢

实现思路 1.一个类,类中维护着一个已登录用户的map,每次登录取出上次该用户对应的session;并给session添加一个过期的属性标识lastHttpSession.setAttribute(BE_KICKED,"您的账号在另一地点登录,您被迫下线") 2.写一个sessionFilter,该sessionFilter取出每个request请求实例中维护的session,判断sessio...

大脸猫的博客 1358

头歌平台 操作系统课堂练习2.3:系统调用

使用 gdb 调试内核,跟踪到 main 函数入口的方法与之前实训一样。系统运行时,执行的前 3 个系统调用的编号和名字分别是什么?系统运行时,执行的前 3 个系统调用的编号和名字分别是什么?本关卡使用版本 1 内核进行分析,版本 1 内核文件存放在。为了完成本关任务,你需要:跟踪系统调用的发生。此时,系统调用号存放在寄存器 eax 中。

lazbuyaotoutu的博客 2万+

Java基础(五)——方法(方法概述、调用图解、方法重载、参数传递),随机点名器,库存管理案例

第一章 方法 1.1方法概述 在java中,方法就是为了实现某一功能或者解决某一问题的办法。方法可能包含很多条语句。 例如打火机,一般人们不会关注其组成结构,原理等方面,人们知道如何使用就可以。打火机也是一个方法,解决了点火问题,其里面也有很多原件。 1.2方法的语法格式 java中,方法的具体语法格式: 修饰符 返回值类型 方法名(参数类型 参数名1,参数类型 参数名2,......){ 执行语句 ……… return 返回值; } 修饰符:方法的修饰符比较...

qq_40243295的博客 606

五段式S型算法笔记

又因为有 v-v0= 0.5jt通式: 所以有 vm-v0= 0.5jtm 所以有4vm-4v0= 2jtm。算法1: v=2vm-v0-0.5*jt+2jt*tm-2jtm*tm。加加速度j 自己设置 已知,vmax自己设置已知;所以有4vm-4v0= 2jtm带入算法1。

qq_36658033的博客 683

基于线性约束最小方差准则(LCMV)的自适应波束形成算法的matlab仿真

波束形成(BeamForming)通过对多路麦克风(常为多路全向麦)信号进行合并,抑制非目标方向信号,增强目标方向信号;进而能够实现对特定方向的聚焦拾音,能够有效提高接收信号的信噪比,也有起到降噪的作用。基本原理是波的干涉:通过调整不同阵列单元信号之间的参数,使得某些角度的信号增强,而另一些角度的信号相互抵消,例如下图中来自±45°的输入信号未被增强,而在0度方向的输入信号同相位叠加后被增强。为了将窄带波束形成扩展至宽带,可以使用频域LCMV波束形成器。

平时工作较为繁忙,私信消息一般晚上回复,谢谢大家~~ 763

cas server + cas client 单点登录 原理介绍

CAS 介绍 CAS 是 Yale 大学发起的一个开源项目,旨在为 Web 应用系统提供一种可靠的单点登录方法,CAS 在 2004 年 12 月正式成为 JA-SIG 的一个项目。CAS 具有以下特点: 开源的企业级单点登录解决方案。 CAS Server 为需要独立部署的 Web 应用。 CAS Client 支持非常多的客户端(这里指单点登录系统中的各个 Web 应用),包括

业精于勤荒于嬉;行成于思,毁于随。 1580

Vue3 - 实现用户上传本地视频文件到服务器+上传进度条+暂停续传+中断取消上传等,视频上传后可在线播放预览已上传的视频,视频文件上传视频回显示例代码(支持Element、Antd等upload组件

实现视频上传和视频预览,vue3实现视频上传功能,上传视频回显,视频流上传播放功能,vue与axios上传视频并显示上传进度,upload组件实现视频文件上传视频回显,upload组件实现视频上传及进度条展示,视频上传及实时进度条显示,视频上传过程中如何中断上传,VUE3+element 上传视频,大文件分片上传+断点续传,axios大文件切片上传,Vue3上传视频后如何播放预览,上传本地视频到后端服务器及在线预览播放视频+移除视频+进度条上传,将已选视频上传服务端并可预览视频,详细实现选择视频并上传到服务

🏆 前端领域优质创作者 3116

CAS单点登录原理分析

1、基本框架图和相关概念 基本框架图: 相关术语概念: 1、结构体系 CAS Server:CAS Server 负责完成对用户的认证工作 , 需要独立部署 , CAS Server 会处理用户名 / 密码等凭证(Credentials)。 CAS Client:负责处理对客户端受保护资源的访问请求,需要对请求方进行身份认证时,重定向到 CAS Server 进行认...

Carson073的博客 904

YOLOv8中的可变形卷积改进 | 从DCNv1到DCNv3的多位置替换可变形卷积保姆级实操

可变形卷积(Deformable Convolution)最早由Dai等人在2017年提出,其核心思想是在标准卷积操作的基础上,引入可学习的偏移量(offsets),使卷积核能够自适应调整其采样位置,从而增强模型对目标变形的感知能力。return x本文通过深入探讨可变形卷积在YOLOv8中的应用,详细介绍了DCNv1、DCNv2和DCNv3的原理和实现,并通过实验验证了其在目标检测任务中的有效性。未来,我们将继续探索更多优化策略和技术,提升YOLOv8模型的性能,以应对更复杂的目标检测任务。

一键难忘的博客 4052

【异常】解决 Claude Code 及第三方插件提示 “API Error: 404“ 的配置修复指南

在使用 Claude 相关的 CLI 工具(如)或 IDE 插件进行开发辅助时,配置错误的模型参数是导致连接失败的常见原因。本文将针对这一典型报错进行复盘与解决。

奶爸的编程之路,也就一周冷个三天 7750

java cas原理_程序员深夜惨遭老婆鄙视,原因竟是CAS原理太简单?| 每一张图都力求精美

悟空聊架构 自主开发了Java学习平台、PMP刷题小程序。整理了一份Java在线学习教程 目前主修Java、多线程、SpringBoot、SpringCloud、k8s。 本公众号不限于分享技术,也会分享工具的使用、人生感悟、读书总结。 关注我免费赠送111本架构师书籍,1000+面试题 联系我:微信搜索:悟空聊架构 邮箱:jackson0585@163.com 爱好:踢球,踢毽子公...

weixin_39612653的博客 65

密码学系列3-Elgamal加密及其IND-CPA安全性证明

但这没有任何影响,敌手本身就只能知道公钥,他并没有能力知道挑战者是否知道对应的私钥。所以上述整个游戏就是一个模拟实验,敌手无法区分挑战者运行的是这个模拟实验,还是真正的按照第一节的算法构造的实验(挑战者知道私钥…为什么合起来的时候有1/2:因为T既可能是随机的也可能是g^{ab},这两种情况概率为1/2。能够攻破Elgamal加密方案,那么挑战者利用敌手的能力构建下面游戏,来求解困难性问题DDH。就是挑战者选的随机数,虽然挑战者自己也不知道。),否则挑战者输出0,作为DDH的回答。

qq_41359358的博客 1251

cas server 踢人功能_程序员深夜惨遭老婆鄙视,原因竟是CAS原理太简单?

关注Java3y本文公众号来源:悟空聊架构作者:悟空聊架构本文已收录至我的GitHub夜黑风高的晚上,一名苦逼程序员正在疯狂敲着键盘,突然他老婆带着一副睡眼朦胧的眼神瞟了下电脑桌面。于是有了如下对话:老婆:这画的图是啥意思,怎么还有三角形,四边形?我:我在画CAS的原理,要不我跟你讲一遍?老婆:好呀!请开始你的表演案例:甲看见一个三角形积木,觉得不好看,想替换成五边形,但是乙想把积木替换...

weixin_42114046的博客 132

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

Java并发必知必会第三弹:用积木讲解ABA原理 |老婆居然又听懂了!

Java并发必知必会第三弹:用积木讲解ABA原理 可落地的 Spring Cloud项目:PassJava 本篇主要内容如下 一、背景 上一节我们讲了程序员深夜惨遭老婆鄙视,原因竟是CAS原理太简单?,留了一个彩蛋给大家,ABA问题是怎么出现的,为什么不是AAB拖拉机,AAA金花,4个A炸弹 ?这一篇我们再来揭开ABA的神秘面纱。 二、面试连环炮 面试的时候我们也经常遭遇面试官的连环追问:...

悟空聊架构的专栏 143

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

《Java并发必知必会》系列

《Java并发必知必会》系列: 1.反制面试官 | 14张原理图 | 再也不怕被问 volatile! 2.程序员深夜惨遭老婆鄙视,原因竟是CAS原理太简单? 3.用积木讲解ABA原理 | 老婆居然又听懂了! 4.全网最细 | 21张图带你领略集合的线程不安全 5.5000字 | 24张图带你彻底理解21种并发锁 6.干货 | 45张图庖丁解牛18种Queue,你知道几种? ...

悟空聊架构的专栏 137

# 全网最细 | 21张图带你领略集合的线程不安全

本篇主要内容如下: 本篇所有示例代码已更新到 我的Github 本篇文章已收纳到我的Java在线文档 《Java并发必知必会》系列: 1.反制面试官 | 14张原理图 | 再也不怕被问 volatile! 2.程序员深夜惨遭老婆鄙视,原因竟是CAS原理太简单? 3.用积木讲解ABA原理 | 老婆居然又听懂了! 4.全网最细 | 21张图带你领略集合的线程不安全 一、线程不安全之ArrayList 集合框架有Map和Collection两大类,Collection下面有List、Set、Queue。L.

悟空聊架构的专栏 601

Java并发必知必会第三弹:用积木讲解ABA原理

作者介绍悟空聊架构种树比较好的时间是十年前,其次是现在。自主开发了Java学习平台、PMP刷题小程序。目前主修Java、多线程、SpringBoot、SpringCloud、k8s。本公...

架构师小秘圈 833

hashmap为什么是线程不安全的_全网最细 | 21张图带你领略集合的线程不安全

作者:悟空聊架构链接:https://juejin.im/post/6866444584688451591本篇主要内容如下:本篇所有示例代码已更新到 我的Github本篇文章已收纳到我的Java再线文档《Java并发必知必会》系列:1.反制面试官 | 14张原理图 | 再也不怕被问 volatile!2.程序员深夜惨遭老婆鄙视,原因竟是CAS原理太简单?3.用积木讲解ABA原理 | 老婆居然又听懂...

weixin_39947016的博客 141

第一次不执行_全网最细 | 21张图带你领略集合的线程不安全

作者:悟空聊架构链接:https://juejin.im/post/6866444584688451591本篇主要内容如下:本篇所有示例代码已更新到 我的Github本篇文章已收纳到我的Java再线文档《Java并发必知必会》系列:1.反制面试官 | 14张原理图 | 再也不怕被问 volatile!2.程序员深夜惨遭老婆鄙视,原因竟是CAS原理太简单?3.用积木讲解ABA原理 | 老婆居然又听懂...

weixin_32743045的博客 89
上一篇: 5000字 | 24张图带你彻底理解Java中的21种锁
下一篇: GitHub上最励志的计算机自学教程
Java思维导图
博客等级 码龄10年 306粉丝 71原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值