JavaEE(系列13) -- 多线程(CAS)

news2024/11/18 18:31:57

目录

1. 什么是 CAS

2. CAS伪代码 

3. CAS 的应用

3.1 实现原子类

3.2 实现自旋锁

4. CAS的ABA问题(面试常问) 

4.1 ABA 问题

4.2 ABA问题解决方案


1. 什么是 CAS

CAS: 全称Compare and swap,字面意思:”比较并交换“.

一个 CAS 涉及到以下操作:

我们假设内存中的原数据V,旧的预期值A,需要修改的新值B。

  • 1. 比较 A 与 V 是否相等。(比较)
  • 2. 如果比较相等,将 B 写入 V。(交换)
  • 3. 返回操作是否成功。

2. CAS伪代码 

下面写的代码不是原子的, 真实的 CAS 是一个原子的硬件指令完成的. 这个伪代码只是辅助理解CAS 的工作流程.

 

        当多个线程同时对某个资源进行CAS操作,只能有一个线程操作成功,但是并不会阻塞其他线程,其他线程只会收到操作失败的信号。

3. CAS 的应用

3.1 实现原子类

标准库中提供了 java.util.concurrent.atomic 包, 里面的类都是基于这种方式来实现的.
典型的就是 AtomicInteger 类. 其中的 getAndIncrement 相当于 i++ 操作.

public class AtomicIntegerTest {
    public static void main(String[] args) throws InterruptedException {
        AtomicInteger num = new AtomicInteger(0);
        Thread t1 = new Thread(()->{
            for (int i = 0; i < 5000; i++) {
                // num++
                num.getAndIncrement();

                // num--
                // num.getAndDecrement();

                // --num
                //num.decrementAndGet();

                // ++num
                //num.incrementAndGet();
            }
        });

        Thread t2 = new Thread(()->{
            for (int i = 0; i < 5000; i++) {
                // num++
                num.getAndIncrement();
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(num.get());
    }
}

两个线程同时调用 getAndIncrement

  • 1) 两个线程都读取 value 的值到 oldValue 中. (oldValue 是一个局部变量, 在栈上. 每个线程有自己的栈)
  • 2) 线程1 先执行 CAS 操作. 由于 oldValue 和 value 的值相同, 直接进行对 value 赋值.
  • 3) 线程2 再执行 CAS 操作, 第一次 CAS 的时候发现 oldValue 和 value 不相等, 不能进行赋值. 因此需要进入循环.在循环里重新读取 value 的值赋给 oldValue
  • 4) 线程2 接下来第二次执行 CAS, 此时 oldValue 和 value 相同, 于是直接执行赋值操作.
  • 5) 线程1 和 线程2 返回各自的 oldValue 的值即可.

3.2 实现自旋锁

 伪代码:

public class SpinLock {
  private Thread owner = null;
  public void lock(){
    // 通过 CAS 看当前锁是否被某个线程持有.
    // 如果这个锁已经被别的线程持有, 那么就自旋等待.
    // 如果这个锁没有被别的线程持有, 那么就把 owner 设为当前尝试加锁的线程.
    while(!CAS(this.owner, null, Thread.currentThread())){
   }
 }
  public void unlock (){
    this.owner = null;
 }
}

 补充:CAS本身就是一个CPU指令,编译器是操作不了的,因而不会有什么内存可见性问题的出现.

内存可见性是:编译器将指令进行调整,把读内存的指令调整为读编译器的指令.

4. CAS的ABA问题(面试常问) 

4.1 ABA 问题

ABA 的问题:


        假设存在两个线程 t1 和 t2. 有一个共享变量 num, 初始值为 A.接下来, 线程 t1 想使用 CAS 把 num 值改成 Z, 那么就需要先读取 num 的值, 记录到 oldNum 变量中.使用 CAS 判定当前 num 的值是否为 A, 如果为 A, 就修改成 Z.但是, 在 t1 执行这两个操作之间, t2 线程可能把 num 的值从 A 改成了 B, 又从 B 改成了 A.

 

 

        线程 t1 的 CAS 是期望 num 不变就修改. 但是 num 的值已经被 t2 给改了. 只不过又改成 A 了. 这个时候 t1 究竟是否要更新 num 的值为 Z 呢?

生活中的例子

假设 滑稽老哥 有 100 存款. 滑稽想从 ATM 取 50 块钱. 取款机创建了两个线程, 并发的来执行 -50
操作.
我们期望一个线程执行 -50 成功, 另一个线程 -50 失败.
如果使用 CAS 的方式来完成这个扣款过程就可能出现问题.


正常的过程
1) 存款 100. 线程1 获取到当前存款值为 100, 期望更新为 50; 线程2 获取到当前存款值为 100, 期
望更新为 50.
2) 线程1 执行扣款成功, 存款被改成 50. 线程2 阻塞等待中.
3) 轮到线程2 执行了, 发现当前存款为 50, 和之前读到的 100 不相同, 执行失败.


异常的过程
1) 存款 100. 线程1 获取到当前存款值为 100, 期望更新为 50; 线程2 获取到当前存款值为 100, 期
望更新为 50.
2) 线程1 执行扣款成功, 存款被改成 50. 线程2 阻塞等待中.
3) 在线程2 执行之前, 滑稽的朋友正好给滑稽转账 50, 账户余额变成 100 !!
4) 轮到线程2 执行了, 发现当前存款为 100, 和之前读到的 100 相同, 再次执行扣款操作
这个时候, 扣款操作被执行了两次!!! 都是 ABA 问题搞的鬼!! 

4.2 ABA问题解决方案

1.约定数据只能进行单方向进行操作 (这个约束性太强)

2.给要修改的值, 引入版本号. 在 CAS 比较数据当前值和旧值的同时, 也要比较版本号是否符合预期.

  1. CAS 操作在读取旧值的同时, 也要读取版本号.
  2. 真正修改的时候
  •                 如果当前版本号和读到的版本号相同, 则修改数据, 并把版本号 + 1.
  •                 如果当前版本号高于读到的版本号. 就操作失败(认为数据已经被修改过了).

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

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

相关文章

速卖通、阿里国际、shopee618盛典!提升订单销量的秘密武器—测评补单技术!

随着电子商务的迅速发展&#xff0c;网络购物已成为现代人生活中不可或缺的一部分。而在众多购物平台中&#xff0c;速卖通凭借其丰富多样的商品选择和优惠的价格政策&#xff0c;成为了全球消费者的首选之一。尤其是每年的618盛典&#xff0c;更是吸引了大量消费者的目光。然而…

【CH32】| 01——新建工程 | 下载 | 运行 |调试

系列文章目录 【CH32】| 00——开发环境搭建 【CH32】| 01——新建工程 | 下载 | 运行 |调试 失败了也挺可爱&#xff0c;成功了就超帅。 文章目录 1. 新建工程1.1 基于官方IDE [MounRiver Studio]1.1.1 使用官方内置的工程模板新建1.1.2 使用自定义工程模板新建1.1.2.1 新建自…

会声会影2023最新版本剪辑视频的方法和步骤

想要学剪辑&#xff0c;剪辑软件是基础。如果大家是零基础的话&#xff0c;建议大家选择一款入门级的剪辑软件&#xff0c;比如说会声会影。这款软件功能强大、操作简单&#xff0c;而且会声会影中文网站首页有很多剪辑教程供大家学习参考。会声会影在用户的陪伴下走过23余载&a…

计算机图形学 | 实验十二:混合(透明物体处理)

计算机图形学 | 实验十二&#xff1a;混合&#xff08;透明物体处理&#xff09; 计算机图形学 | 实验十二&#xff1a;混合&#xff08;透明物体处理&#xff09;混合&#xff08;Blending&#xff09;开启混合和设置混合模式绘制顺序排序透明物体绘制实验结果 华中科技大学《…

IPO观察丨德尔玛上市,“极致单品”模式的一场胜利

近日&#xff0c;创新电器品牌德尔玛&#xff08;广东德尔玛科技股份有限公司&#xff0c;代码“301332”&#xff09;&#xff0c;正式登陆深圳证券交易所创业板&#xff0c;发行价格为14.81元/股。上市前&#xff0c;德尔玛已完成了5亿元A轮融资及3.3亿元A轮融资&#xff0c;…

Linux Ubuntu配置CPU与GPU版本tensorflow库的方法

本文介绍在Linux操作系统的发行版本Ubuntu中&#xff0c;配置可以用CPU或GPU运行的Python新版本深度学习库tensorflow的方法。 在文章Anaconda配置Python新版本tensorflow库&#xff08;CPU、GPU通用&#xff09;的方法&#xff08;https://blog.csdn.net/zhebushibiaoshifu/ar…

Elasticsearch 7.x 基本操作 (CRUD)

1.概述 Elasticsearch 是一个流行的开源搜索引擎&#xff0c;用于存储、搜索和分析数据。下面是 Elasticsearch 7.x 版本的基本操作&#xff08;CRUD&#xff09;&#xff1a; 1、创建索引&#xff1a; PUT /index_name {"settings": {"number_of_shards"…

最近公司招了一个华为拿30K出来的,真是砂纸擦屁股,给我露了一手....

今年的金三银四已经结束&#xff0c;很多小伙伴收获不错&#xff0c;拿到了心仪的 offer。 各大论坛和社区里也看见不少小伙伴慷慨地分享了常见的面试题和八股文&#xff0c;为此咱这里也统一做一次大整理和大归类&#xff0c;这也算是划重点了。 俗话说得好&#xff0c;他山…

【C++】位图(海量数据处理)

文章目录 抛出问题:引入位图位图解决 位图的概念位图的实现结构构造函数设置位清空位判断这个数是否存在反转位size与count打印函数 位图的应用 抛出问题:引入位图 问题&#xff1a;给40亿个不重复的无符号整数&#xff0c;没排序&#xff0c;给一个无符号整数&#xff0c;如何…

基于 MapReduce 的分布式计算系统

访问【WRITE-BUG数字空间】_[内附完整源码和文档] 本文以 MapReduce 为基础&#xff0c;实现了一套基于浏览器实现的分布式系统。加之如今 Chrome 对各个平台近乎完美的兼容性&#xff0c;实现了一次编写&#xff0c;处处运行的目标。同时得力于个人移动设备的普及&#xff0c…

java云HIS系统源码 医院HIS管理系统源码 Java医院系统源码 SaaS医院his系统源码

技术框架&#xff1a; 1、前端&#xff1a;AngularNginx 2、后台&#xff1a;JavaSpring&#xff0c;SpringBoot&#xff0c;SpringMVC&#xff0c;SpringSecurity&#xff0c;MyBatisPlus&#xff0c;等 3、数据库&#xff1a;MySQL MyCat 4、缓存&#xff1a;RedisJ2Cac…

day6 - 使用图像运算进行图像美化

本期将了解图像的基础运算&#xff0c;包含算数运算和位运算等。我们所使用的图像处理技术其实都是靠一些简单的基础运算来完成的&#xff0c;例如加法运算、位运算等&#xff0c;这些简单运算是我们后续研究更复杂的图像处理的基础。 完成本期内容&#xff0c;你可以&#xf…

HiveSQL基础练习题

HiveSQL基础练习题 1.环境准备1.1建表语句1.2数据准备1.3插入数据 2.查询2.1 查询姓名中带“华”的学生名单2.2 查询姓“王”老师的个数2.3 检索课程编号为“04”且分数小于60的学生学号&#xff0c;结果按分数降序排列2.4 查询语文成绩 < 90分的学生和其对应的成绩&#xf…

day16 Servlet交互作用域ELJSTL

转发和重定向 **作用:**为了让jsp和servlet做到责任分离,用于web组件的跳转 **web组件:**jspservlet 转发的方法 request.getRequestDispatcher("跳转的地址").forward(request,response)**跳转的位置:**在服务端进行跳转 重定向的方法 response.sendRedirect(…

2.9 playwright之python实现

1、目录结构如下 2、main.py import os import shutilfrom playwright.sync_api import sync_playwright from config.setting import config from utils.template import Template from utils.md5 import Md5 from utils.delete import del_files import pytest from utils.d…

面试被问麻了...

前几天组了一个软件测试面试的群&#xff0c;没想到效果直接拉满&#xff0c;看来大家对面试这块的需求还是挺迫切的。昨天我就看到群友们发的一些面经&#xff0c;感觉非常有参考价值&#xff0c;于是我就问他还有没有。 结果他给我整理了一份非常硬核的面筋&#xff0c;打开…

全网最全性能测试总结,分析性能测试问题+性能调优方案...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 性能分析和优化一…

【录用案例】2区毕业快刊仅34天录用,新增8篇录用、9篇见刊、13篇检索

2023年5月13日-2023年5月19日&#xff0c;经核实&#xff0c;由我处Unionpub学术推荐的8篇论文已被期刊部录用、9篇见刊、13篇检索&#xff1a; 2区系统类SSCI 【期刊简介】IF:2.5-3.0&#xff0c;JCR2区&#xff0c;中科院4区 【检索情况】SSCI 在检&#xff0c;正刊 【征稿…

本地项目上传到Git(Gitee)仓库

一、步骤解答&#xff08;详细图解步骤见第二大点&#xff09; 1、打开我们的项目所在文件夹&#xff0c;我们发现是不存在.git文件 2、在你的项目文件夹外层【鼠标右击】弹出菜单&#xff0c;在【鼠标右击】弹出的菜单中&#xff0c;点击【Git Bash Here】&#xff0c;弹出运…

循环队列+OJ题之设计循环队列

生命不是要等待风暴过去&#xff0c;而是要学会在风暴中跳舞。 ——卡莉尔吉布朗目录 &#x1f33a;前言&#xff1a; &#x1f341;一.循环队列是什么&#xff1f; &#x1f34f;二.循环队列有什么作用&#xff1f; &#x1f340;三.OJ题之设计循环队列 1…