当前位置: 首页 > news >正文

多线程基础(二)CAS无锁优化/自旋锁/乐观锁、ABA问题

CAS (Compare And Set)比较并替换

上篇文章的锁问题解决,可以使用更高效的方法,使用AtomXXX类,AtomXXX类本身方法都是原子性的,但不能保证多个方法连续调用是原于性的。

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;public class AtomicIntegerTest {AtomicInteger count = new AtomicInteger(0);void m1 (){for(int i=0;i<10000;i++){count.incrementAndGet();  // 相当于线程安全的 count++}}public static void main(String[] args) {AtomicIntegerTest t = new AtomicIntegerTest();List<Thread> Threads = new ArrayList<>();for(int i=0;i<10;i++){Threads.add(new Thread(t::m1,"Thread-"+i));}Threads.forEach((o) -> o.start());Threads.forEach((o) -> {try {o.join();} catch (InterruptedException e) {e.printStackTrace();}});System.out.println(t.count);}
}

结果:

100000

 那为什么  count.incrementAndGet(); 是线程安全的? 点进去看它的源码可知:

底层使用了 weakCompareAndSetInt(o, offset, v, v + delta)  方法,这个方法就是 CAS。

CAS 概念:

cas方法一共有三个参数:

        V:要改的值(内存位置)

        Expected:期望值(预期原值)

        NewValue:要设定的新值

如果 要改的值 v 和 expected 的值是一样的,则将 v的值改为新值 newValue。如果不是一样的,则循环下一次比较或直接返回失败。

注:因为 cas方法是 CPU原语支持的,即 在比较替换的过程中是不可以被打断的,所以不会出现 比较成功的时候,其他线程将要改的值或者新值替换的问题。

ABA问题

aba问题是:在进行比较并替换的时候,将a值改为b,然后马上将b再改回a,这样的话,比较是可以成功的,但是对于原值a,它的版本已经不是原始的了。

如果这个a的值是基础类型则没什么关系。但是如果是Object类型,比如:a引用 b,b引用c,这是一个线程将 a引用到了c,并将c的一些属性做了修改,再将a引用到b。这个时候,业务执行的一些逻辑会导致各种问题的出现。如果还没懂,引入一个经典的例子:

一个小偷,把别人家的钱偷了之后又还了回来,还是原来的钱吗,你老婆出轨之后又回来,还是原来的老婆嘛?ABA问题也一样,如果不好好解决就会带来大量的问题。最常见的就是资金问题,也就是别人如果挪用了你的钱,在你发现之前又还了回来。但是别人却已经触犯了法律。

那怎么解决呐?

案例重现:

    private static AtomicInteger index = new AtomicInteger(10);public static void main(String[] args) {new Thread(() -> {index.compareAndSet(10, 11);index.compareAndSet(11, 10);System.out.println(Thread.currentThread().getName()+": 10->11->10");},"张三").start();new Thread(() -> {try {TimeUnit.SECONDS.sleep(2);boolean isSuccess = index.compareAndSet(10, 12);System.out.println(Thread.currentThread().getName()+": index是否为预期值:10,"+isSuccess+"   设置的新值是:"+index.get());} catch (InterruptedException e) {e.printStackTrace();}},"李四").start();}

张三: 10->11->10
李四: index是否为预期值:10,true   设置的新值是:12

通过:AtomicStampedReference 添加版本号 解决这个问题

  private static AtomicInteger index = new AtomicInteger(10);static AtomicStampedReference<Integer> stampRef= new AtomicStampedReference(10, 1);public static void main(String[] args) {new Thread(() -> {int stamp = stampRef.getStamp();System.out.println(Thread.currentThread().getName()+ " 第1次版本号: " + stamp);stampRef.compareAndSet(10, 11,stampRef.getStamp(),stampRef.getStamp()+1);System.out.println(Thread.currentThread().getName()+ " 第2次版本号: " + stampRef.getStamp());stampRef.compareAndSet(11, 10,stampRef.getStamp(),stampRef.getStamp()+1);System.out.println(Thread.currentThread().getName()+ " 第3次版本号: " + stampRef.getStamp());},"张三").start();new Thread(() -> {try {int stamp = stampRef.getStamp();System.out.println(Thread.currentThread().getName()+ " 第1次版本号: " + stamp);TimeUnit.SECONDS.sleep(2);boolean isSuccess =stampRef.compareAndSet(10, 12,stampRef.getStamp(),stampRef.getStamp()+1);System.out.println(Thread.currentThread().getName()+ " 修改是否成功: "+ isSuccess+" 当前版本 :" + stampRef.getStamp());System.out.println(Thread.currentThread().getName()+ " 当前实际值: " + stampRef.getReference());} catch (InterruptedException e) {e.printStackTrace();}},"李四").start();}

 张三 第1次版本号: 1
李四 第1次版本号: 1
张三 第2次版本号: 2
张三 第3次版本号: 3
李四 修改是否成功: true 当前版本 :4
李四 当前实际值: 12

这里使用的是AtomicStampedReference的compareAndSet函数,这里面有四个参数:

compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp)。

(1)第一个参数expectedReference:表示预期值。

(2)第二个参数newReference:表示要更新的值。

(3)第三个参数expectedStamp:表示预期的时间戳。

(4)第四个参数newStamp:表示要更新的时间戳。

重现案例即解决方法原文地址:解决CAS机制中ABA问题的AtomicStampedReference详解 - 知乎 (zhihu.com)

总结:所有以 AtomXXX开头的类,底层都是使用cas方法,并是通过 Unsafe类实现的。

Unsafe类的出现等于c/c++的指针,给 java语言赋予了 原来 C/C++实现的指针方法。比如:

allocateMemory 、freeMemory等操作内存的方法。

补充:Atomic、Sync、LongAdder的比较

耗时比较:Sync < Atomic < LongAdder

线程数大、循环数大的情况下,使用 LongAdder,优势很明显。LongAdder底层使用的是分段锁+cas方式。将大量线程分段执行,最后相加。

线程数小、循环数小的情况下,用Atomic

相关文章:

多线程基础(二)CAS无锁优化/自旋锁/乐观锁、ABA问题

CAS &#xff08;Compare And Set&#xff09;比较并替换 上篇文章的锁问题解决&#xff0c;可以使用更高效的方法&#xff0c;使用AtomXXX类&#xff0c;AtomXXX类本身方法都是原子性的&#xff0c;但不能保证多个方法连续调用是原于性的。 import java.util.ArrayList; imp…...

记ABAC的落地实践

为什么使用ABAC 一般提到授权&#xff0c;我们就会想到角色&#xff08;role&#xff09;。什么样的用户拥有什么样的角色可以怎么操作什么样的资源&#xff0c;这是我们普遍使用的权限系统的模型。这里的角色实质上是包含了一组用户操作资源的规则集合。一旦角色被创建&#…...

【C++】C++11线程库 和 C++IO流

春风若有怜花意&#xff0c;可否许我再少年。 文章目录 一、C11线程库1.thread类介绍2.mutex互斥锁 和 CAS原子操作&#xff08;compare and set&#xff09;3.lock_guard和unique_lock4.两个线程交替打印&#xff0c;一个打印奇数&#xff0c;一个打印偶数&#xff08;线程同步…...

cpp11实现线程池(六)——线程池任务返回值类型Result实现

介绍 提交任务函数submitTask中返回的Result类型应该是用Result类包装当前的task&#xff0c;因为出函数之后task即如下形式&#xff1a;return Result(task); Result和Task都要互相持有对方的指针&#xff0c;Task要将任务执行结果通过Result::setVal(run()) 调用传给其对应…...

道岔外锁闭装置介绍

简述 道岔外锁闭装置是一种能可靠地锁闭尖轨和基本轨的器械。它能有效地克服尖轨在密贴时的转换阻力&#xff0c;即使连接杆折断&#xff0c;外锁闭装置仍在起着锁闭作用。外锁闭能够隔离列车通过时对转换设备的振动和冲击&#xff0c;提高转换设备寿命和可靠性。 产品分类 …...

idea把项目上传到码云

1. 为项目创建仓库 2. 选中中项目右击git, 先add, 在commit Directory 3. 设置远程码云项目地址 4. push项目, ok。 注意&#xff1a; 如果你在最后push出现以下提示&#xff0c;则说明提交失败 Push to origin/master was rejected(译文&#xff1a;推送到原点/master被拒绝…...

设计模式之责任链模式

责任链模式的定义是&#xff1a;使多个对象都有机会处理请求&#xff0c;从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链&#xff0c;并沿着这条链传递该请求&#xff0c;直到有对象处理它为止。 责任链模式适合于请求需要经过多个处理器&#xff0c;并…...

Python--我一般都用这个模块压缩文件

打包成压缩文件很多时候都能用上&#xff0c;也包括了自动化中的部分应用。例如&#xff0c;将测试报告打包发送。 本章就来介绍其中一个模块&#xff0c;可以用于结合上一章的内容结合使用。 from zipfile import ZipFile ❝ ZipFile是zipfile的一个方法。 ❞ 提取zip文件 fro…...

Chapter8 :Physical Constraints(ug903)

8.1About Physical Constraints&#xff08;关于物理约束&#xff09; XilinxVivado集成设计环境&#xff08;IDE&#xff09;允许通过设置对象属性值对设计对象进行物理约束。示例包括&#xff1a; •I/O约束&#xff0c;如位置和I/O标准 •布局约束&…...

星标3.5k,一款国产的轻量级开源在线项目任务管理工具

今天给大家推荐一个轻量级的开源在线项目任务管理工具&#xff1a;DooTask 图片 DooTask 提供各类文档协作工具、在线思维导图、在线流程图、项目管理、任务分发、即时IM&#xff0c;文件管理等工具。 高效便捷的团队沟通工具 针对项目和任务建立群组&#xff0c;工作问题可…...

【华为OD机试真题2023B卷 JAVA】字符串摘要

华为OD2023(B卷)机试题库全覆盖,刷题指南点这里 字符串摘要 知识点字符串排序 时间限制:1s 空间限制:256MB 限定语言:不限 题目描述: 给定一个字符串的摘要算法,请输出给定字符串的摘要值。 1、去除字符串中非字母的符号。 2、如果出现连续字符(不区分大小写),则输…...

Java线程概述 (一)线程介绍

文章目录 &#x1f412;个人主页&#x1f3c5;JavaSE系列专栏&#x1f4d6;前言&#xff1a;&#x1fa85;什么是程序 、进程、线程&#xff1f;&#x1fa85;线程的生命周期&#x1fa85;多线程&#x1fa85;守护者线程&#x1fa85;线程并行与并发&#x1fa85;死锁&#x1f…...

操作系统第三章——存储系统(下)

锦衣雪华玉颜色&#xff0c;回眸一笑天下倾 文章目录 3.2.1 虚拟内存的基本概念知识总览传统存储方式的特征&#xff0c;缺点局部性原理虚拟内存的定义如何实现虚拟内存技术知识总结 3.2.2 请求分页管理方式知识总览页表机制缺页中断机制地址变换机制知识回顾 3.2.3 页面置换算…...

初识结构体

目录 结构体的声明 结构体的基础知识 结构体的声明 结构体成员的类型 结构体变量的定义和初始化 定义 初始化 结构体成员的访问 结构体变量访问成员 结构体指针访问指向变量的成员 结构体传参 传地址 传结构体 结论 结构体的声明 结构体的基础知识 数组&#xff…...

协程并发下数据汇总:除了互斥锁,还有其他方式吗?

1. 简介 本文介绍了在并发编程中数据汇总的问题&#xff0c;并探讨了在并发环境下使用互斥锁和通道两种方式来保证数据安全性的方法。 首先&#xff0c;通过一个实例&#xff0c;描述了一个并发拉取数据并汇总的案例&#xff0c;并使用互斥锁来确保线程安全。然后&#xff0c…...

5、Ray-Actor模型和并发编程

5、Ray-Actor模型和并发编程 导航 1.简介和背景 2.Ray的基本概念和核心组件 3.分布式任务调度和依赖管理 4.对象存储和数据共享 5.Actor模型和并发编程 6.Ray的高级功能和扩展性 7.使用Ray构建分布式应用程序的案例研究 8.Ray社区和资源 9.核心框架介绍...

HNU-电路与电子学-小班2

第二次讨论 讨论题目&#xff1a; 1、电子秤的电桥电路可以分别用 1 个压控电阻、 2 个压控电阻、 3 个压控电阻、 4 个压控电阻实现吗&#xff1f;试写出每种实现的 U AB 输出表达式&#xff0c;并分析哪种实现电桥 电压的灵敏度&#xff08;SV/ △ R &#xff09;高。 …...

二分图匹配算法

匈牙利算法、Hopcroft-Karp算法和Kuhn-Munkres算法是三种常见的二分图匹配算法&#xff0c;它们在实现方式、时间复杂度和适用场景上有所差异。以下是它们的区别和优缺点&#xff1a; 匈牙利算法&#xff1a; 实现方式&#xff1a;匈牙利算法使用深度优先搜索(DFS)来寻找增广路…...

虹科技术 | 虹科EtherCAT增量编码器输入模块数据采集实操测试

1. 背景介绍 编码器是将信号或数据进行编制、转换为可用以通讯、传输和存储的信号形式的设备。编码器把角位移或直线位移转换成电信号&#xff0c;前者称为码盘&#xff0c;后者称为码尺。按照读出方式编码器可以分为接触式和非接触式两种&#xff1b;按照工作原理编码器可分为…...

2023.05.21 学习周报

文章目录 摘要文献阅读1.题目2.背景3.现存问题和解决方法4.方法4.1 Variational mode decomposition (VMD)4.2 Bidirectional LSTM 5.实验5.1 数据标准化5.2 评价指标5.3 实验过程及结果 6.结论和展望 优劣解距离法有限元1.求解一个简单的传热问题2.有限元如何实现 总结 摘要 …...

第19节 Node.js Express 框架

Express 是一个为Node.js设计的web开发框架&#xff0c;它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用&#xff0c;和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型

CVPR 2025 | MIMO&#xff1a;支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题&#xff1a;MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者&#xff1a;Yanyuan Chen, Dexuan Xu, Yu Hu…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】

微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来&#xff0c;Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件

今天呢&#xff0c;博主的学习进度也是步入了Java Mybatis 框架&#xff0c;目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学&#xff0c;希望能对大家有所帮助&#xff0c;也特别欢迎大家指点不足之处&#xff0c;小生很乐意接受正确的建议&…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版​分享

平时用 iPhone 的时候&#xff0c;难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵&#xff0c;或者买了二手 iPhone 却被原来的 iCloud 账号锁住&#xff0c;这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

第25节 Node.js 断言测试

Node.js的assert模块主要用于编写程序的单元测试时使用&#xff0c;通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试&#xff0c;通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

高危文件识别的常用算法:原理、应用与企业场景

高危文件识别的常用算法&#xff1a;原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件&#xff0c;如包含恶意代码、敏感数据或欺诈内容的文档&#xff0c;在企业协同办公环境中&#xff08;如Teams、Google Workspace&#xff09;尤为重要。结合大模型技术&…...

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...