AtomicInteger保证线程安全

news/2024/11/3 3:42:52 标签: java, 算法, 数据结构, jvm, 面试, 原子操作

建议再看这篇文章之前可以先去了解一下jvm内存模型~

这里现提出一个问题,多线程环境下,我们怎么去保证i++的操作是线程安全的?

通过这个问题,我们引入今天要说的一个原子操作类——AtomicInteger,他可以实现各种原子性的操作,说到原子操作,不知道大家有没有了解过CAS,这个也可以用来做原子操作,既然都可以,那各位可以想想他们之间有什么联系~

我们先来看一段代码

java">@SpringBootTest
public class JVMTest {
    public static void main(String[] args) {
        int a= 3;
        //AtomicInteger a=new AtomicInteger(3);
        Person p=new Person();
        p.id=12;
        p.name="yinan";
        getData(a,p);
        System.out.println("主线程:"+a+" "+p.name);
    }
    public static void getData(int a, Person p){
       // a.getAndIncrement();
        a++;
        p.name="yinan-change";
        System.out.println(a+" "+p.name);
    }
}
class Person{
    int id;
    String name;
}

在我们使用int作为a的基本类型的时候,以及我创建的对象Person,我们调用方法getData修改了对应的值之后,最后的在main方法中输出结果是什么呢?给我可以先思考一下,我这里就直接给运行截图了:

你会发现在最后main方法中打印出来的a依然是原来的值,这是因为java内存模型中基本数据类型是直接放在栈当中的,栈是线程内所有的,所以按照栈的特性,先进后出最后再方法里面输出的是4,当然方法结束后栈自然就被销毁了,由于栈是线程内的,所以自然不会同步到main方法当中的a;

那我们再来看看对象,对象的实例是放在堆当中的,而堆里面存放的是线程间共享的数据,相当于对象在创建的时候会在线程里面创建一个对象引用,然后指向堆中的那个对象,如果我的线程里面的这个对象数据被修改过后,那么就会通知到堆中的对象并进行修改

那我们该怎么去处理这种基本数据类型在一个线程中修改了,在其他线程中也能同步进行修改呢?

这里我们可以引出几种方法供各位参考:

  • 采用数据的方法,将a转换为数组的方式,因为数组也是存放在堆当中的
  • 使用AtomicInteger来实现
  • 创建具有a类型的属性的对象
  • 使用Optional

这里我们就讲一下AtomicInteger:

我们将a的类型改为AtomicInteger后,你会发现最后再main方法里面a的值也同时进行修改,看懂了怎么用之后我们再来看一下底层实现:

AtomicInteger底层采用Unsafe和volatile来保证线程安全和可见性,volatile这个关键字在前面有讲过,不理解怎么用的可以去看看:volatile关键字-CSDN博客

我们知道java方法是不能直接调用底层操作系统的,所以需要使用本地方法来进行操作,而Unsafe就相当于是c++中的指针,可以直接操作内存,所以这样就做到了操作内存。

当我们调用getAndIncrement方法的时候

我们再进入getAndAddInt方法

我们发现这个方法在Unsafe类下面,这个方法主要实现就是判断var1和var5的值是否相等,如果相等那就把var5+var4,var4就是++1操作,然后返回,如果不相等那就循环比较

这里再说一下CAS,这个东西主要是用来做乐观锁和锁自旋的,底层实现就是通过Unsafe来实现的,所以使用AtomicInteger是线程安全的,可以保证原子性操作,再加上对value使用了valotile关键字,保证了可见性,因此我在方法中对数据进行操作后可以同步到主内存当中共其他线程调用。

整个步骤大概流程是这样,中间可能有一些没有讲清楚的地方,可以在评论区说出你的疑问,我们共同学习。

最后,提出一个问题,CAS既然是用来保证线程安全的,那为什么不能使用synchronized来实现呢?


http://www.niftyadmin.cn/n/5735950.html

相关文章

使用Python高效处理CSV和Excel文件的多种方法

在数据分析、自动化和日常开发中,CSV和Excel文件是非常常见的数据存储格式。Python提供了强大的工具来读取、编辑和保存这两种文件,满足从基本读取到复杂分析的需求。本文将深入介绍CSV和Excel文件的多种处理方法,帮助你更好地管理和分析数据…

告别传统办公软件,这款编辑器让你事半功倍!

文章目录 1 界面的多样性2 性能优化3 文档编辑器的新功能4 外部文本支持5 体验感想 ONLYOFFICE最近发布了文档8.2版本,带来了众多新特性和性能改进。作为一名用户和开发者,我对这些更新进行了深入的体验,感受到了不少亮点。 新版本特别强调了…

学习正则表达式,如何校验手机号与电子邮箱

文章目录 一、正则表达式基础知识1.特殊字符(Metacharacters)2.字符类(Character Classes)3.预定义字符集(Predefined character classes)4.分组(Groups)5.量词(Quantifi…

【JavaScript】axios 二次封装拦截器(接口、实例、全局)

学习 coderwhy 老师结合 ts 二次封装 axios 目录结构 config config\index.ts // export const BASE_URL "http://codercba.com:9002"; export const TIME_OUT 10000;// 1. 根据环境变量区分接口地址 // let BASE_URL: string; // if (process.env.NODE_ENV &qu…

[HNCTF 2022 Week1]calc_jail_beginner_level3(JAIL)

开启靶场,打开链接,下载附件: 嗯,直接显示“Oh hacker!”,有点懵,先看看下载的附件(server.py): 得到信息如下: 用户输入的表达式长度被限制在7个字符以内。…

Golang--DOS命令、变量、基本数据类型、标识符

1、DOS命令 切换盘符(大小写没有区别): c: //切换到c盘 d: //切换到d盘 e: //切换到e盘 显示文件内信息: 指令:dir 切换路径: 指令:cd 绝对路径 / 相对路径 . 表示当前…

【Linux指令】---获取进程的PID

获取进程的PID getpid()函数

DB-GPT系列(二):DB-GPT部署(镜像一键部署、源码部署)

一、简介 DB-GPT 是一个开源项目,其将大语言模型 LLM 与数据库紧密结合。该项目主要致力于探索如何让预训练的大规模语言模型(例如 GPT)能够直接与数据库进行交互,从而生成更为准确且信息丰富的回答。 DB-GPT部署后能否直接使用…