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

本文主要是介绍程序员深夜惨遭老婆鄙视,原因竟是CAS原理太简单?| 每一张图都力求精美,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

作者介绍

悟空
种树比较好的时间是十年前,其次是现在。
自主开发了Java学习平台、PMP刷题小程序。目前主修Java多线程SpringBootSpringCloudk8s
本公众号不限于分享技术,也会分享工具的使用、人生感悟、读书总结。

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

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

我:我在画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的原理。

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

彩蛋:还有一个ABA问题没有给大家讲,另外这里怎么不是AAB(拖拉机),AAA(金花)?

4个A

这周前三天写技术文章花了大量时间,少熬夜,睡觉啦 ~ 我们下期再来讲ABA问题,小伙伴们分享转发下好吗?您的支持是我写作最大的动力~

- END -
推荐阅读
1. 为什么建议大家使用Linux开发?爽(外加七个感叹号)2.  除了百度网盘,我还有它,内测资格开放申请,非会员下载 10MB/s3. 8月撸书|当当花160买400,不撸白不撸之前博主分享了很多资源,有的已经删除了(你懂得),如果有的你当时没有领到还想领得就可以加我微信回复“权限”获取spring security与spingmvc分布式权限管理系统好文章,我在看

这篇关于程序员深夜惨遭老婆鄙视,原因竟是CAS原理太简单?| 每一张图都力求精美的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/189127

相关文章

一份LLM资源清单围观技术大佬的日常;手把手教你在美国搭建「百万卡」AI数据中心;为啥大模型做不好简单的数学计算? | ShowMeAI日报

👀日报&周刊合集 | 🎡ShowMeAI官网 | 🧡 点赞关注评论拜托啦! 1. 为啥大模型做不好简单的数学计算?从大模型高考数学成绩不及格说起 司南评测体系 OpenCompass 选取 7 个大模型 (6 个开源模型+ GPT-4o),组织参与了 2024 年高考「新课标I卷」的语文、数学、英语考试,然后由经验丰富的判卷老师评判得分。 结果如上图所

回调的简单理解

之前一直不太明白回调的用法,现在简单的理解下 就按这张slidingmenu来说,主界面为Activity界面,而旁边的菜单为fragment界面。1.现在通过主界面的slidingmenu按钮来点开旁边的菜单功能并且选中”区县“选项(到这里就可以理解为A类调用B类里面的c方法)。2.通过触发“区县”的选项使得主界面跳转到“区县”相关的新闻列表界面中(到这里就可以理解为B类调用A类中的d方法

自制的浏览器主页,可以是最简单的桌面应用,可以把它当成备忘录桌面应用

自制的浏览器主页,可以是最简单的桌面应用,可以把它当成备忘录桌面应用。如果你看不懂,请留言。 完整代码: <!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><ti

python实现最简单循环神经网络(RNNs)

Recurrent Neural Networks(RNNs) 的模型: 上图中红色部分是输入向量。文本、单词、数据都是输入,在网络里都以向量的形式进行表示。 绿色部分是隐藏向量。是加工处理过程。 蓝色部分是输出向量。 python代码表示如下: rnn = RNN()y = rnn.step(x) # x为输入向量,y为输出向量 RNNs神经网络由神经元组成, python

【新闻】AI程序员要来了吗?阿里云官宣

内容提要 6 月 21 日,在阿里云上海 AI 峰会上,阿里云宣布推出首个AI 程序员。 据介绍,这个AI程序员具备架构师、开发工程师、测试工程师等多种岗位的技能,能一站式自主完成任务分解、代码编写、测试、问题修复、代码提交整个过程,最快分钟级即可完成应用开发,大幅提升研发效率。 近段时间以来,有关AI的实践应用突破不断,全球开发者加速研发步伐。有业内人士坦言,随着大模型性能逐渐提升,AI应

警告,恶意域名疯狂外联,原因竟然是……

前言 &nbsp;&nbsp; 在某个风和日丽的下午,突然收到客户那边运维发过来的消息说我司的DTA设备在疯狂告警,说存在恶意域名外联,我急忙背上小背包前往客户现场,经过与客户协同排查,最终确定该事件为一起挖矿病毒引起的恶意域名外联事件。(因客户信息保密且为了保证文章逻辑完整性,部分截图为后期追加图) 事件分析 一看域名地址donate.v2.xmrig.com

数据库原理与安全复习笔记(未完待续)

1 概念 产生与发展:人工管理阶段 → \to → 文件系统阶段 → \to → 数据库系统阶段。 数据库系统特点:数据的管理者(DBMS);数据结构化;数据共享性高,冗余度低,易于扩充;数据独立性高。DBMS 对数据的控制功能:数据的安全性保护;数据的完整性检查;并发控制;数据库恢复。 数据库技术研究领域:数据库管理系统软件的研发;数据库设计;数据库理论。数据模型要素 数据结构:描述数据库

宝塔面板部署青龙面板教程【简单易上手】

首先,你得有一台部署了宝塔面板的服务器(自己用本地电脑也可以)。 宝塔面板部署自行百度一下,很简单,这里就不走流程了,官网版本就可以,无需开心版。 首先,打开宝塔面板的软件商店,找到下图这个软件(Docker管理器)安装,青龙面板还是安装在docker里,这里依赖宝塔面板安装和管理docker。 安装完成后,进入SSH终端管理,输入代码安装青龙面板。ssh可以直接宝塔里操作,也可以安装ssh连接

计算机组成原理——RECORD

第一章 概论 1.固件  将部分操作系统固化——即把软件永恒存于只读存储器中。 2.多级层次结构的计算机系统 3.冯*诺依曼计算机的特点 4.现代计算机的组成:CPU、I/O设备、主存储器(MM) 5.细化的计算机组成框图 6.指令操作的三个阶段:取指、分析、执行 第二章 计算机的发展 1.第一台由电子管组成的电子数字积分和计算机(ENIAC) 第三章 系统总线

华为某员工爆料:偷偷跑出去面试,被面试官鄙视了。第一句话就问:华为淘汰的吧,35岁了,这个年龄在华为能混得下去吗?身体没啥毛病吧

“你都35岁了,难不成是被华为淘汰的?在华为混不下去了吧?身体没啥毛病吧,我们这体检可是很严的。” 近日,一位华为员工在朋友圈爆料,自己在面试时遭到了面试官的无理取闹和人身攻击,原因仅仅是因为他35岁了,曾经在华为工作过。 这番话,充满了傲慢与偏见,让人听了义愤填膺。这位面试官的言行,不仅是对求职者的不尊重,更是对职场规则的践踏。 面试本应是双向选择的过程,企业和求职者在相互了解的基