关于java中CAS会引发的ABA问题探究

在并发环境下,为了保证并发安全问题,通常我们会进行加锁操作,比如加上synchronized关键字。但是很多情况下,我们不需要这样的重量级锁,比如说多个线程对某个int类型变量i

进行++操作,但是不加锁吧,又怕影响结果,因为i++不是一个原子操作,会出现并发问题,我们来看个案例。

 1 public class NonAtomicIncrementDemo {
 2     // 共享变量
 3     private static int i = 0;
 4 
 5     public static void main(String[] args) throws InterruptedException {
 6         // 线程数量
 7         int threadCount = 10;
 8         // 每个线程执行的操作次数
 9         int incrementCount = 1000;
10 
11         // 创建线程数组
12         Thread[] threads = new Thread[threadCount];
13 
14         // 创建并启动线程
15         for (int j = 0; j < threadCount; j++) {
16             threads[j] = new Thread(() -> {
17                 for (int k = 0; k < incrementCount; k++) {
18                     // 非原子操作 i++
19                     i++;
20                 }
21             });
22             threads[j].start();
23         }
24 
25         // 等待所有线程执行完毕
26         for (Thread thread : threads) {
27             thread.join();
28         }
29 
30         // 预期结果
31         int expectedResult = threadCount * incrementCount;
32         // 实际结果
33         int actualResult = i;
34 
35         System.out.println("预期结果: " + expectedResult);
36         System.out.println("实际结果: " + actualResult);
37     }
38 }

执行的结果

 问题原因:由于 i++ 不是原子操作,多个线程可能会同时读取 i 的值,然后对其进行加 1 操作,最后再写回。这样就可能会导致某些线程的加 1 操作被覆盖,从而使最终结果小于预期值

解决方法:可以使用 Java 提供的原子类(如 AtomicInteger)来保证 i++ 操作的原子性,或者使用 synchronized 关键字来对 i++ 操作进行同步。

以下是使用 AtomicInteger 解决该问题的示例代码:

 1 import java.util.concurrent.atomic.AtomicInteger;
 2 
 3 public class AtomicIncrementDemo {
 4     // 原子整数
 5     private static AtomicInteger atomicInteger = new AtomicInteger(0);
 6 
 7     public static void main(String[] args) throws InterruptedException {
 8         // 线程数量
 9         int threadCount = 10;
10         // 每个线程执行的操作次数
11         int incrementCount = 1000;
12 
13         // 创建线程数组
14         Thread[] threads = new Thread[threadCount];
15 
16         // 创建并启动线程
17         for (int j = 0; j < threadCount; j++) {
18             threads[j] = new Thread(() -> {
19                 for (int k = 0; k < incrementCount; k++) {
20                     // 原子操作 incrementAndGet()
21                     atomicInteger.incrementAndGet();
22                 }
23             });
24             threads[j].start();
25         }
26 
27         // 等待所有线程执行完毕
28         for (Thread thread : threads) {
29             thread.join();
30         }
31 
32         // 预期结果
33         int expectedResult = threadCount * incrementCount;
34         // 实际结果
35         int actualResult = atomicInteger.get();
36 
37         System.out.println("预期结果: " + expectedResult);
38         System.out.println("实际结果: " + actualResult);
39     }
40 }

 

在这个示例中,使用 AtomicInteger 的 incrementAndGet() 方法来保证 i++ 操作的原子性,从而避免了线程安全问题。

incrementAndGet底层就是用了CAS操作。

CAS 的基本思路就是, 如果这个地址上的值和期望的值相等, 则给其赋予新 值, 否则不做任何事儿,但是要返回原值是多少。 自然 CAS 操作执行完成时, 在 业务上不一定完成了, 这个时候我

们就会对 CAS 操作进行反复重试, 于是就有了 循环 CAS。很明显, 循环 CAS 就是在一个循环里不断的做 cas 操作, 直到成功为 止。 Java 中的 Atomic 系列的原子操作类的实现则是利用了循环 CAS来实现。
 
 
 

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DHTMLX Gantt 甘特图导出全数据图-----自实现方式过程记录

针对上一篇DHTMLX Gantt甘特图导出全数据图,使用官方提供的方法虽然很方便,也免费,但每次生成图片的时候需要访问下:export.dhtmlx.com的相关授权接口,这对我们的项目如果是在内网(不能联网访问外网的情况)非常不友好,也就无法使用官方的方式了。 那么如果尽量能实现同…

jmter

1.安装 官网下载解压 2.改中文 bin/jmeter.properties 加入 language=zh_CN3.运行 bin/jmeter.bat 4.压测使用 线程组 -> HTTP请求 , HTTP请求头管理线程组 -> 汇总结果线程组属性线程数:配置几个就代表有几个虚拟用户 Ramp-Up 时间(秒):表示从第一个虚拟用户开始生成…

Dify + Ollama + DeepSeek 知识库部署

通过ollama安装deepseek-r1我没有GPU服务器,安装7b版本小模型试试。参考: https://github.com/ollama/ollama 安装ollama # ollama默认端口11434 curl -fsSL https://ollama.com/install.sh | sh ollama pull deepseek-r1:7b # 查看本地模型 ollama list ollama run deepseek-…

2025.2.20(IDEA的入门使用)

今天10点看了视频学习IDEA的基本使用方法。现在来记录一下。 https://www.bilibili.com/video/BV1gb42177hm?spm_id_from=333.788.videopod.episodes&p=7&vd_source=5573907594eed8092d5b30e989fa0415 IDEA创建Java的过程如图:一个大工程里面包含了各个模块,每个模块…

【Spring Security编程】Spring Security自定义配置

一、基于内存的用户认证 1.1 创建自定义配置 实际开发的过程中,我们需要应用程序更加灵活,可以在SpringSecurity中创建自定义配置文件 官方文档: Java自定义配置(https://docs.spring.io/spring-security/reference/servlet/configuration/java.html) UserDetailsService用来…

linux(ubantu)虚拟机上运行c语言程序保姆级教程(附sudo apt-get update 报错连接失败的一种解决方法)

虚拟机使用的是VMware Workstation+Ubuntu 1.安装GCC 在桌面上右键打开终端然后分别输入 sudo apt-get update sudo apt-get install build-essential 注意:输入后会让你输入password,此时输入任何字符屏幕上会什么都没有,但是实际上已经输入进去了,所以直接盲输完密码按回…

flutter3-trip仿携程酒店预订|Flutter3.27+Getx预约旅游酒店App程序

基于Flutter3.x+Dart3+GetX跨平台仿携程/飞猪旅行酒店客房预订查询app系统。 flutter3_trip原创2025新版flutter3.27.1+dart3.6+getx+flutter_datepicker跨平台仿携程/飞猪/同程旅游app酒店客房预订系统。实现了首页、酒店预订模块、酒店搜索/列表/详情、探索动态、订单、消息聊…

【Linux应用】Linux服务器访问不了外网如何安装软件?

大多数服务器都是在隔离的网络环境中,服务器被限制访问互联网。原因当然是出于安全考虑,一旦服务器有外网访问能力,中毒后会主动更新病毒库。比如各种广告、XX全家桶就是这么来的。 那如果想在服务器上安装软件怎么办❓ 可能有人会说先提前下载好,再传上去不就行了嘛。这对…

国产免费的网络监控与分析系统工具

科来网络分析系统(CSNAS)是一款集数据包采集、协议解码、流量分析、故障诊断、安全检测等功能于一体的专业网络管理工具。其核心能力包括实时监控网络流量、识别异常行为、还原会话数据流以及提供多维度性能分析,帮助用户快速定位网络故障、优化性能并提升安全性。 系统要求…

本地md博客自动化上传博客园

该工具会在你这个文档同一目录下生成一个`xxx-cnblog.md`文件,这个就是在他自动帮你上传了图片后,替换了你本地图片的路径,替换为cnblog的图床url路径。前言叠甲:先帮博客园免费打个广告,要用这个方法也往博客园发文喔~ 我的博客园:https://www.cnblogs.com/dhan 需求 经…

cpu的核心数和线程数

在windows操作系统下,想看看自己cpu的核数,我打开任务管理器-性能-CPU,看到我的cpu是2核。 然后,我打开计算机管理-设备管理器-处理器,发现这里显示四个cpu信息,但这四个不是内核数,是线程数。一、概念: 1. 核心数(Cores):CPU实际的物理核心数量,比如双核、四核、六…

WebService服务需要通过注册路由进行转发到自身的asmx页面问题

背景要求 三方接口自定义了url路径,但是原先代码是通过webservice进行开发的asmx页面。 前面已经有人写好了代码,但是本地部署死活无法通过注册的路由调用! `protected void Application_Start(object sender, EventArgs e) { RegisterRoutes(RouteTable.Routes);}/// <su…