JVM学习-JMM

目录

1.什么是JMM

2.JMM怎样保障数据的可见性、有序性、原子性

        2.1保证原子性

        2.2.保证可见性

        2.3保证有序性

3.CAS

        3.1乐观锁和悲观锁

        3.2 CAS介绍

4.重量级锁的自旋优化


1.什么是JMM

        JMM即Java内存模型 ,定义了一套在多线程读写共享数据(如数组、成员变量等)时,对数据的可见性、有序性、原子性的规则和保障。JMM和Java内存结构关系不大,内存结构就是JVM的内存组成、垃圾回收以及字节码和类加载的一些技术。

2.JMM怎样保障数据的可见性、有序性、原子性

        2.1保证原子性

        对于多个线程共享的数据而言,如果不对数据进行加锁,那么多个线程在调用同一资源时很可能出现异常操作。

        比如线程1和线程2分别对一个初始值为0的变量 i 执行 i++ 和 i-- 操作,理论上执行的结果是0,但实际情况却不一定。由于操作系统在处理线程时采用的是时间片轮转的方式,很有可能出现线程1还未执行完 i++ 时间片就耗尽了,接下来就轮到线程2执行 i-- 操作。线程2在时间片内完成了对 i 的修改,此时 i 的值为-1。然后再次轮到线程1,由于线程1拿到的 i 的初始值是0,所以 i++ 是在 i=0 的基础上执行的,执行完成后 i 的值为1,覆盖掉了线程2的执行结果-1,所以相当于线程2对于 i 的修改是无效的。

        这就需要进行加锁,当一个线程对一个共享资源未使用完毕前其他线程不能使用这一资源,也就是保障了对共享资源的操作在执行完之前不会有其他线程访问这一资源。
        加锁操作:

synchronized(要加锁的对象){需要保障原子性的代码
}

        锁粗化:在加锁时要尽量扩大加锁的范围,比如对变量 i 执行一千次加1操作,如果仅对加一的操作进行加锁,那么就会执行一千次加锁和解锁的操作;但如果是对整个循环进行加锁,不仅可以保证原子性,而且只用执行一次加锁和解锁的操作,能够缩短运行时间。

        2.2.保证可见性

        共享资源存储在主内存中,当一个线程频繁读取某一资源时JIT(即时编译器)会进行优化,将该资源的值存入到该线程工作内存中的高速缓存中,后面再读取这个资源的值时会从这个高速缓存中读取,虽然提高了效率,但当对这一资源的值进行修改后,还是会从高速缓存中读取之前的旧值,导致线程对这个资源的修改不可见。
        有两种方式保证可见性:

  • 使用synchronized加锁

        在加锁时会清空工作内存中对所有共享资源的缓存,强制要求到主内存中重新读取共享变量的值,保证了拿到的共享变量的值是最新的。并且在解锁时,无论是否对共享变量的值进行了修改,都会将共享变量的值刷新回主内存,以确保主内存中的共享变量的值是最新的。也可以直接调用System.out.println(),因为执行输出操作时会调用到synchronized。

        优点是既能确保原子性又能确保可见性,缺点就是synchronized属于重量级的操作,性能较低。

  • 使用volatile关键字

        对共享变量加上volatile关键字能够强制要求线程在读取共享变量的值时总是从主内存中读取,保证了可见性。该方式性能较高,但仅适用于一个写线程其他都是读线程的情况,因为如果有多个写线程由于不能保证原子性就无法确定拿到的值是否是正确的。

        2.3保证有序性

        JVM在执行赋值操作时会根据指令是否耗时而进行指令重排,在不影响结果的条件下调整指令的执行顺序,但在并发执行时更改指令的执行顺序就可能会出现错误,此时可以通过加上volatile关键字禁用指令重排来保证有序性。

3.CAS

        3.1乐观锁和悲观锁

        在介绍CAS之前先来了解一下乐观锁和悲观锁:

        3.2 CAS介绍

        CAS即Compare And Swap,是一种乐观锁的思想,不使用synchronized对共享变量加锁,通过volatile关键字的配合实现了无锁并发。原理是通过一个while循环不断尝试修改共享变量的值,在循环时暂时存储该共享变量的原值和本线程修改后的值,并通过compareAndSwap方法来判断该线程修改共享变量结束后存储的原值是否和当前共享变量的值相同。如果不同说明在该线程修改共享变量期间其他线程对共享变量的值进行了修改,那么本次修改就是无效的,需要再次进入循环重新获取共享变量的值并尝试修改;如果相同说明修改期间其他线程并没有对共享变量进行修改,那么就会将修改的结果更新到主内存中,并返回true,可以通过if语句实现跳出循环。

        至于要volatile关键字的配合是因为要保证可见性,在每次循环时都能获取到共享变量最新的值。
        由于synchronized加锁会使线程进入阻塞,并进行上下文切换,需要保存线程阻塞前的状态并在被唤醒时恢复,这一过程是非常耗时的,所以CAS无锁并发能够提升效率;但CAS适用于线程竞争不激烈并且是多核CPU的情况下,因为当竞争比较激烈时肯定会进行频繁的循环,此时就需要花费大量的时间来不断尝试修改共享变量的值;而如果是单核CPU,那么当时间片耗尽时只能等待下次拿到时间片时才能继续尝试,但多核CPU就能实现在其他线程运行的同时不断尝试,这才能体现CAS的优势。

4.重量级锁的自旋优化

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

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

相关文章

openssl3.2 - note - Writing OpenSSL Provider Skeleton

文章目录 openssl3.2 - note - Writing OpenSSL Provider Skeleton概述笔记测试工程的建立复现的provider工程总结Provider包含的头文件openssl/core.h中的数据结构实现 OSSL_provider_init()看一下openssl自带的提供者provider的openssl命令行测试provider的本质是hook了opens…

插入排序:一种简单而有效的排序算法

插入排序:一种简单而有效的排序算法 一、什么是插入排序?二、插入排序的步骤三、插入排序的C语言实现四、插入排序的性能分析五、插入排序的优化六、总结 在我们日常生活和工作中,排序是一种非常常见的操作。比如,我们可能需要对一…

【 c 语言 】指针入门

🎈个人主页:豌豆射手^ 🎉欢迎 👍点赞✍评论⭐收藏 🤗收录专栏:C语言 🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共同学习、交流进步&…

ChatGPT编程—实现小工具软件(文件查找和筛选)

ChatGPT编程—实现小工具软件(文件查找和筛选) 今天借助[小蜜蜂AI][https://zglg.work]网站的ChatGPT编程实现一个功能:根据特定需求结合通配符和其他条件来进行文件查找和筛选。在这个例子中,我们将创建一个函数find_files,它接受用户输入的…

RK3568平台开发系列讲解(基础篇)内核是如何发送事件到用户空间

🚀返回专栏总目录 文章目录 一、相关接口函数二、udevadm 命令三、实验沉淀、分享、成长,让自己和他人都能有所收获!😄 一、相关接口函数 kobject_uevent 是 Linux 内核中的一个函数, 用于生成和发送 uevent 事件。 它是 udev 和其他设备管理工具与内核通信的一种方式。…

设计模式学习笔记 - 规范与重构 - 8.实践:程序出错返回啥?NULL、异常、错误吗、空对象?重构ID生成器,处理各函数的异常

概述 我们可以把函数的运行结果分为两类。一类是预期结果,也就是正常情况下输出的结果。一类是非预期的结果,也就是函数在异常(或出错)情况下输出的结果。 在正常情况下,函数返回数据的类型非常明确,但是…

【Spring Boot】创建你的第一个 Spring Boot 应用

创建你的第一个 Spring Boot 应用 1.环境配置2.步骤详解3.项目结构分析3.1 入口类 DemoApplication3.2 控制器 PathVariableController3.3 控制器 BasicController3.4 模型 User 4.运行 Spring Boot 目前已经成为了 Java 开发领域的框架范式。本篇博客,我将带领大家…

layui table列表重载后保持进度条位置不变

使用layui的table表格组件时,当我们操作了某行的修改后,刷新了页面,进度条则跳回到最上面。 除了layui高版本应该内置有方法解决了此问题,但是低版本需要另外想办法解决。 具体解决方式如下: 1.在编辑操作成功前&am…

【List、Set、数据结构、Collections】-Collections

day03 【List、Set、数据结构、Collections】 5.3 简述Comparable和Comparator两个接口的区别。 Comparable:强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的compareTo方法被称为它的自然比较方法。只能在类中实现compareTo…

【Linux的 yum_vim工具篇】

Linux学习笔记---004 Linux的yum_vim工具篇1、Linux软件包管理器yum1.1、yum是什么?1.2、软件包是什么?软件包是谁给提供的呢?1.3、Linux系统(其中的centos 7)生态 2、yum的相关操作2.1、yum基本指令格式2.2、软件源 3、yum的本地配置3.1、配…

Lua-Lua与C++的交互2

Lua与C的交互是指在C程序中使用Lua本语言,或者在Lua脚本中调用C代码的过程。这种交互可以实现C与Lua之间的数据传递和函数调用。 在C中与Lua交互的主要步骤如下: 引入Lua库:首先需要在C程序中引入Lua的头文件和库文件,以便能够使…

pytorch中张量变换函数

在PyTorch中view(), transpose() 和 permute() 函数都是用于改变张量(Tensor)维度结构的,但它们的作用和使用场景有所不同。 torch.view() 功能:该函数用于将一个张量重塑为新的形状,但它必须保持原有元素数量不变。它…

【MASM汇编语言快速入门】8086MASM汇编深入理解指令对标志位的影响

8086MASM汇编深入理解指令对标志位的影响 文章目录 8086MASM汇编深入理解指令对标志位的影响0. 指令对标志位影响1. 指令对标志位影响速查表2. flags标志寄存器: 标志位含义解读flags1. 状态标志cf, pf, af, zf, sf, of2. 控制标志df, if, tf 详解:1. 传送指令2. 算…

如何在IDEA 中设置背景图片

在IDEA 中设置背景图片,可以按照以下步骤操作: 1、打开 IntelliJ IDEA 软件,进入代码编辑主界面。 点击编辑窗口上方的“File”菜单项。 2、在下拉子菜单中,选择“Settings”选项(如果你使用的是 macOS,可…

AUTOSAR汽车电子嵌入式编程精讲300篇-车载 CAN 总线延时特性分析及优化(续)

目录 3.1.2 通信错误恢复时间 3.2 延时指标研究 3.2.1 总线 Burst 情况 3.2.2 抖动

vue3子父组件之间的调用

子组件&#xff1a; capacityIndex.vue 父组件&#xff1a; index.vue A.子组件获取父组件属性 1.在父组件中引用子组件 import capacityIndex from "./capacityIndex"; <capacityIndex :tankInfo"tankInfo" :deviceNameInfo"deviceNameInfo…

Spark-Scala语言实战(1)

在之前的文章中&#xff0c;我们学习了如何在Linux安装Spark以及Scala&#xff0c;想了解的朋友可以查看这篇文章。同时&#xff0c;希望我的文章能帮助到你&#xff0c;如果觉得我的文章写的不错&#xff0c;请留下你宝贵的点赞&#xff0c;谢谢。 Spark及Scala的安装https:/…

【智能算法】保姆级教程-如何使用CEC测试集,以及如何定义自己的优化问题

目录 1.准备工作2.使用CEC2005测试集3.自定义优化问题-无约束问题4.自定义优化问题-有约束问题5.代码实现 1.准备工作 一个CEC2005测试集 一个测试智能算法&#xff0c;比如麻雀搜索算法SSA 2.使用CEC2005测试集 以CEC2005测试集函数F7为例&#xff1a; 3.自定义优化问题…

solr/ES 分词插件Jcseg设置自定义词库

步骤&#xff1a; 1、找到配置文件jcseg-core/target/classes/jcseg.properties修改配置&#xff1a; 下载地址: https://gitee.com/lionsoul/jcseg#5-如何自定义使用词库 lexicon.path {jar.dir}/../custom-word 设置lexicon路径&#xff0c;我们这个配置可以自定义&#xf…

python非常优秀的图结构处理框架

核心特点 多样性: 支持创建多种类型的图,如无向图、有向图、多重图等。 灵活性: 易于扩展和定制,支持自定义节点和边的属性。 易用性: 提供大量的算法和绘图工具,便于网络分析和可视化。 最佳实践 安装 Networkx: pip install networkx功能一:创建和操作图 标题:基础…