AtomicInteger 详细解读

news2024/12/23 15:18:17

AtomicInteger 详细解读

一、原始数据并发写引发的问题

对于共享变量整数的加减操作,当出现并发的情况时,很容易造成线程不安全。

1、代码示例

public class Demo {
    static int num = 0;

    public static void main(String[] args) throws InterruptedException {
        List<Thread> list = new ArrayList<>();
        for (int i = 0; i < 2; i++) {
            Thread t = new Thread() {
                @Override
                public void run() {
                    for (int i = 0; i < 10000; i++) {
                        num++;
                    }
                }
            };
            t.start();
            list.add(t);
        }

        for (Thread thread : list) {
            thread.join();
        }

        System.out.println(num);
    }
}

运行结果如下图,结果并不是预期的20000,出现并发问题:
在这里插入图片描述

2、原因分析

  1. 全局变量num作为共享资源:程序运行时,每个线程都会读取num的当前值,然后对其进行加1操作。当多个线程并发执行时,它们可能几乎同时读到num的同一个值,然后各自加1,最后写回。这样就导致了部分加1操作丢失,因为多个线程实际上是基于相同的初始值执行加法,而不是基于上一个线程已经更新后的值。

  2. 缺乏同步机制:在多线程编程中,为了防止这种数据竞争(data race)问题,通常需要使用锁或其他同步工具来确保同一时间只有一个线程能够访问和修改共享资源。

二、使用AtomicInteger改写

​ 上述示例中,我们可以使用锁机制,来保证线程安全,但锁的粒度太大,会造成一定程度的性能损耗,推荐使用 AtomicInteger。

改写代码如下:

public class Demo {

//    static int num = 0;
    static AtomicInteger num = new AtomicInteger();

    public static void main(String[] args) throws InterruptedException {
        List<Thread> list = new ArrayList<>();
        for (int i = 0; i < 2; i++) {
            Thread t = new Thread() {
                @Override
                public void run() {
                    for (int i = 0; i < 10000; i++) {
//                        num++;
                        num.getAndIncrement();
                    }
                }
            };
            t.start();
            list.add(t);
        }

        for (Thread thread : list) {
            thread.join();
        }

        System.out.println(num);
    }
}

运行结果:

在这里插入图片描述

三、 AtomicInteger底层原理

源码追踪
在这里插入图片描述

AtomicInteger实现线程安全的自增,从底层原理出发,举例来说:

​ 当线程A和线程B同时尝试对AtomicInteger的值进行自增操作时,如果出现了并发情况,我们可以这样理解其底层原理及如何确保线程安全的:

  1. 初始状态:假设AtomicInteger的当前值为X

  2. 并发尝试

    • 线程A读取到当前值X
    • 几乎同时,线程B也读取到了相同的值X(因为两个线程读操作之间没有互斥,这是并发冲突的根源)。
  3. CAS操作介入

    • 线程A操作:线程A尝试通过CAS操作将值从X更改为X+1。这个操作包括三个步骤:检查当前值是否仍为期望值X,如果是,则更新为X+1;如果不是(即值已经被其他线程改变),则操作失败。
    • 假设线程A的CAS操作成功,此时AtomicInteger的值变为X+1
  4. 线程B的处理

    • 线程B随后尝试执行同样的CAS操作,但因为线程A已经将值更新为X+1,线程B发现当前值不再等于它期望的旧值X,因此线程B的CAS操作失败。
    • CAS操作失败后,线程B通常会重新读取当前值(现在已经是X+1),然后再次尝试CAS操作,这次期望值为X+1,试图将其更新为X+2。这个重试过程确保了线程B最终能够成功执行自增,并且不会丢失更新。

通过这样的机制,AtomicInteger确保了即使在高并发情况下,每个线程的自增操作都能够正确反映到最终结果中,从而实现了线程安全的自增。这个过程是非阻塞的,意味着线程不会因为等待锁而被挂起,提高了效率。

设计思想:

​ 对于悲观锁,认为数据发生并发冲突的概率很大,读操作之前就上锁。synchronized关键字、ReentrantLock都是悲观锁的典型。

​ 对于乐观锁,认为数据发生并发冲突的概率比较小,读操作之前不上锁。等到写操作的时候,再判断数据在此期间是否被其他线程修改了。如果被其他线程修改了,就把数据重新读出来,重复该过程; 如果没有被修改,就写回去。判断数据是否被修改,同时写回新值,这两个操作要合成一个原子操作, 也就是CAS ( Compare And Set )。 AtomicInteger的实现就是典型的乐观锁。

程修改了,就把数据重新读出来,重复该过程; 如果没有被修改,就写回去。判断数据是否被修改,同时写回新值,这两个操作要合成一个原子操作, 也就是CAS ( Compare And Set )。 AtomicInteger的实现就是典型的乐观锁。

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

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

相关文章

【Redis】Redis面试和工作中十有八九会遇到的问题

1. 数据类型 常用的Redis数据类型有5种&#xff0c;分别是&#xff1a; String、List、Set、SortedSet、Hash 还有一些高级数据类型&#xff0c;比如Bitmap、HyperLogLog、GEO等&#xff0c;其底层都是基于上述5种基本数据类型。因此在Redis的源码中&#xff0c;其实只有5种数…

2000-2001年中国净生态系统生产力产品(Net ecosystem productivity, NEP)

简介 中国净生态系统生产力产品(Net ecosystem productivity, NEP)是总初级生产力GPP&#xff08;单位时间内生物通过光合作用所固定的有机碳量&#xff09;扣除自养生物呼吸消耗的部分和异养生物呼吸消耗&#xff08;土壤呼吸&#xff09;光合产物之后的部分&#xff0c;是生…

连锁收银系统如何助力实体门店私域运营

作为实体门店&#xff0c;私域运营是提升客户黏性和增加复购率的重要策略之一。而连锁收银系统在私域运营中扮演了关键的角色&#xff0c;它不仅可以帮助门店管理客户信息和消费记录&#xff0c;还能够通过数据分析和营销功能提供个性化的服务和推广活动。下面看看连锁收银系统…

TypeScript中的泛型(Generics)

TypeScript中的泛型&#xff08;Generics&#xff09; 在前面的几篇文章中&#xff0c;我们了解了TypeScript的类、接口和基本的数据类型系统。本文将重点介绍TypeScript中的泛型&#xff0c;这是一种强大的工具&#xff0c;它允许我们创建可重用的组件&#xff0c;同时保持类…

信创电脑|暴雨新增兆芯KX-7000处理器版本

IT世界 5 月 15 日消息&#xff0c;暴雨公司信创家族新上架了一款搭载兆芯KX-7000系列处理器、摩尔线程8GB 显卡、16G DDR5 内存以及 512G SSD 的新配置台式电脑主机。 兆芯 KX-7000 处理器采用开先的 8 核 Chiplet互联架构&#xff0c;最高频率3.7 GHz&#xff0c;拥有 32MB 的…

11种最常见的网络安全攻击类型!

常见的网络安全攻击类型有很多&#xff0c;其中包括DOS和DDOS攻击、中间人攻击、SQL注入攻击、勒索攻击、网络攻击、蛮力攻击等&#xff0c;接下来这篇文章为大家介绍一下{BANNED}最佳常见的11种网络安全攻击类型&#xff0c;快来看看吧。 1、DOS和DDOS攻击 拒绝服务攻击旨在使…

IDEA 使用Alibaba Cloud Toolkit 实现远程 自动部署

安装插件 maven方式部署 配置服务器主机信息 配置发布到主机 单击Select 单击run 就可以将选择module的jar文件上传到服务器的指定位置了 Alibaba Cloud Toolkit 上传文件的方式部署

网工内推 | 测试工程师,NA认证以上,15薪,补充医疗险

01 天视通 招聘岗位&#xff1a;测试工程师 职责描述&#xff1a;1、网络视频监控相关软件产品测试&#xff0c;及行测试记录和相应各种文档资料/手册编写&#xff1b;2、负责编写测试计划、测试用例、搭建测试环境、执行测试&#xff1b;3、进行BUG验证根据测试结果&#xff…

腾讯云环境安装单机版minio

Minio 下载安装 wget https://dl.min.io/server/minio/release/linux-amd64/minio修改minio 文件为可执行文件 chmod x minio3、启动&#xff0c;随机端口启动 ./minio server /data/miniodata # 或者指定密码执行 MINIO_ACCESS_KEYmyminioadmin MINIO_SECRET_KEYmyminioadm…

UV胶固化时使用的UV灯要如何选择才适合!

近日&#xff0c;在使用UV灯固化 UV胶的过程中&#xff0c;遇到了不同的问题&#xff0c;最基本的就是很多人对于固化UV胶时&#xff0c;使用什么样的UV灯不清楚&#xff0c;从而导致了UV胶在实际使用过程中没有固化&#xff0c;或者没有完全固化&#xff0c;胶水仍处是液体流动…

代码随想录算法训练营第三十一天|455.分发饼干,376. 摆动序列,53. 最大子序和

455.分发饼干 优先把小饼干分给胃口值小的&#xff0c;或者是把大饼干分给胃口大的。 376. 摆动序列 class Solution { public:int wiggleMaxLength(vector<int>& nums) {if (nums.size() < 1) return nums.size();int curDiff 0; // 当前一对差值int preDiff …

五大方法教你如何分分钟构造百万测试数据!

在测试的工作过程中&#xff0c;很多场景是需要构造一些数据在项目里的&#xff0c;方便测试工作的进行&#xff0c;构造的方法有很多&#xff0c;难度和技术深度也不一样。本文提供方法供你选择。 在测试的工作过程中&#xff0c;很多场景是需要构造一些数据在项目里的&…

[ciscn 2022 东北赛区]math

1.题目 import gmpy2 from Crypto.Util.number import * from flag import flag assert flag.startswith(b"flag{") assert flag.endswith(b"}") messagebytes_to_long(flag) def keygen(nbit, dbit):if 2*dbit < nbit:while True:a1 getRandomNBitIn…

Py深度学习基础|关于Batch Normalization

1. 为什么需要Batch Normalization 通常我们会在输入层进行数据的标准化处理&#xff0c;这是为了让模型学习到更好的特征。同样&#xff0c;在模型的中间层我们也可以进行normalize。在神经网络中, 数据分布对训练会产生影响。 比如我们使用tanh作为激活函数&#xff0c;当输入…

微信小程序picker 组件 region 本地数据源

目录 mac系统获取 windows系统获取 总结 mac系统获取 打开文件目录 Contents/Resources/package.nw/js/libs/region_sub_strict 代码转换 let pcaData 复制出来的数据。。。。const pca pcaData.split(\n);const arr [];const getDepth (line) > line.trim().split(\t…

动态路由-链路状态路由协议ospf案例

实验拓扑和要求如图 ospf实验 1.设置各个接口地址 2.测试ar5到ar6的连通性 3.配置ospf协议&#xff0c;routerid&#xff0c;area&#xff0c; 详细的网络信息&#xff0c;等待网络收敛后&#xff0c; 查看ospf信息&#xff0c;路由表信息&#xff0c;再次测试连通性 注意区域…

C++动态内存区域划分、new、delete关键字、泛型编程、函数模版、类模版

目录 一、C/C中程序的内存区域划分 为什么会存在内存区域划分&#xff1f; 二、new关键字 1、内置类型的new/delete使用方法&#xff1a; 2、new和delete的本质 3、常见面试题——malloc/free和new/delete的区别 三、模版 1、泛型编程 2、函数模版 &#xff08;1&…

ASP.NET在线毕业论文提交系统的设计与实现

摘 要 本设计就很好的解决了上面的问题&#xff0c;它不但能实现毕业生论文的在线提交&#xff1b;还能给教师一定的权限&#xff0c;以在线的方式对自己指导的学生的论文进行审核&#xff1b;并且管理员还可以方便的将每个学生的论文信息按统一的论文排版本格式导出成word文…

大模型LLM 结合联网搜索增强isou

参考&#xff1a; https://github.com/yokingma/search_with_ai 在线使用网址&#xff1a; https://isou.chat/ 安装github下载&#xff0c;运行docker compose 如果一直报下面错误&#xff1a; 解决方法https://github.com/yokingma/search_with_ai/pull/7 默认打开&a…

阿里云VOD视频点播流程(2)

二、视频点播 1、入门代码 基于OSS原生SDK上传 &#xff0c;参考文档&#xff1a;https://help.aliyun.com/zh/vod/user-guide/upload-media-files-by-using-oss-sdks?spma2c4g.11186623.0.0.1f02273fj4lxNJ 视频点播面向开发者提供了丰富的上传方式&#xff0c;其中上传SDK&…