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://xiahunao.cn/news/2869344.html

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

相关文章

贪心算法(算法竞赛、蓝桥杯)--糖果传递

1、B站视频链接&#xff1a;A31 贪心算法 P2512 [HAOI2008] 糖果传递_哔哩哔哩_bilibili 题目链接&#xff1a;[HAOI2008] 糖果传递 - 洛谷 #include <bits/stdc.h> using namespace std; const int N1000005; int n,a[N],c[N]; long long b,ans;int main(){scanf(&quo…

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:UIExtensionComponent (系统接口))

UIExtensionComponent用于支持在本页面内嵌入其他应用提供的UI。展示的内容在另外一个进程中运行&#xff0c;本应用并不参与其中的布局和渲染。 通常用于有进程隔离诉求的模块化开发场景。 说明&#xff1a; 该组件从API Version 10开始支持。后续版本如有新增内容&#xff0…

QT C++ QButtonGroup应用

//QT 中&#xff0c;按钮数量比较少&#xff0c;可以分别用各按钮的信号和槽处理。 //当按钮数量较多时&#xff0c;用QButtonGroup可以实现共用一个槽函数&#xff0c;批量处理&#xff0c;减少垃圾代码&#xff0c; //减少出错。 //开发平台&#xff1a;win10QT6.2.4 MSVC…

Xilinx FPGA模式配置

Xilinx FPGA模式配置 Xilinx UltraScale FPGA有7种配置模式&#xff0c;由模式输入引脚M[2:0]决定。七种模式如图1所示。 图1 7种配置模式 7种模式可分为3大类&#xff0c; 1、JTAG模式&#xff08;可归为从模式&#xff09;&#xff1b; 2、主模式&#xff1b; 3、从模式…

基于SpringBoot SSM vue办公自动化系统

基于SpringBoot SSM vue办公自动化系统 系统功能 登录 个人中心 请假信息管理 考勤信息管理 出差信息管理 行政领导管理 代办事项管理 文档管理 公告信息管理 企业信息管理 会议室信息管理 资产设备管理 员工信息管理 开发环境和技术 开发语言&#xff1a;Java 使用框架: S…

OpenCV4.9.0开源计算机视觉库在 Linux 中安装

返回目录&#xff1a;OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;OpenCV 环境变量参考 下一篇&#xff1a;将OpenCV与gcc和CMake结合使用 引言&#xff1a; OpenCV是一个开源的计算机视觉库&#xff0c;由英特尔公司所赞助。它是一个跨…

【LeetCode热题100】146. LRU 缓存(链表)

一.题目要求 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类&#xff1a; LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存int get(int key) 如果关键字 key 存在于缓存中&#xff0c;则返回关键字的值&#xff0c…

wsl or 虚拟机 安装

1.wsl2安装 WSL全称Windows Subsystem for Linux&#xff0c;是微软开发的适用于Linux的Windows子系统 如今已经有两代了&#xff1a; 所以用的多的还是wsl2。 安装前需要先去设置启用或关闭Windows功能&#xff1a; 打开适用于linux的子系统和虚拟机平台。 Microsoft Store里…

matlab simulink 工业现场漆包机烘炉模糊PID控制算法

1、内容简介 略 70-可以交流、咨询、答疑 2、内容说明 matlab simulink 工业现场漆包机烘炉模糊PID控制算法 第五章 控制算法仿真与分析 在第二章系统分析结合工业现场漆包机烘炉数学模型详细分析模糊PID控制算法以及解耦控制算法的优缺点、实现原理&#xff0c;为本章算法…

Jmeter---分布式

分布式&#xff1a;多台机协作&#xff0c;以集群的方式完成测试任务&#xff0c;可以提高测试效率。 分布式架构&#xff1a;控制机&#xff08;分发任务&#xff09;与多台执行机&#xff08;执行任务&#xff09; 环境搭建&#xff1a; 不同的测试机上安装 Jmeter 配置基…

Acwing-基础算法课笔记之动态规划(线性DP)

Acwing-基础算法课笔记之动态规划&#xff08;线性DP&#xff09; 一、数字三角形1、概述2、闫氏dp分析法代码示例 二、最长上升子序列1、概述2、闫氏dp分析法3、过程模拟4、代码演示 三、最长上升子序列强化版1、概述2、代码示例 四、最长公共子序列&#xff08;LCS&#xff0…

FFmpeg查看所有支持的编码/解码器/封装/解封装/媒体格式/滤镜

查看所有支持的编码器与解码器 ffmpeg -codecs 只查看所有编码器: ffmpeg -encoders 只查看所有解码器: ffmpeg -decoders 只查看H264编码器: ffmpeg -h encoderh264 只查看H264解码器: ffmpeg -h decoderh264 查看所有支持的封装: ffmpeg -muxers 查看所有支持的解封装…

三.使用java的API文档

在Java中&#xff0c;API是指“应用程序接口”&#xff08;Application Programming Interface&#xff09;。Java API是Java编程语言中提供的类和接口的集合&#xff0c;用于开发各种类型的应用程序。类比C的STL&#xff08;标准模板库&#xff09;。 通俗理解就当做些封装好…

Spark-Scala语言实战(1)

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

如何在Windows11上安装WSL和Linux子系统以及搭建Docker环境

今天给大家介绍一下如何在Windows11上安装Docker 打开控制面板&#xff1a; 打开程序&#xff1a; 打开启用或关闭Windows功能。 勾选Linux子系统&#xff1a; 此时&#xff0c;可能需要重启电脑。 以管理员身份打开PowerShell执行&#xff1a; bcdedit /set hyperv…

C语言学习过程总结(18)——指针(6)

一、数组指针变量 在上一节中我们提到了&#xff0c;指针数组的存放指针的数组&#xff0c;那数组指针变量是什么呢&#xff1f; 显而易见&#xff0c;数组指针变量是指针 同样类比整型指针变量和字符指针变量里面分别存放的是整型变量地址和字符变量地址&#xff0c;我们可以…

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

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

redis中List和hash数据类型

list类型是用来存储多个有序的字符串的&#xff0c;列表当中的每一个字符看做一个元素&#xff0c;一个列表当中可以存储一个或者多个元素&#xff0c;redis的list支持存储2^32-1个元素。redis可以从列表的两端进行插入&#xff08;pubsh&#xff09;和弹出&#xff08;pop&…

springboot多模块下swaggar界面出现异常(Knife4j文档请求异常)或者界面不报错但是没有显示任何信息

继上一篇博文&#xff0c;我们解决了多模块下扫描不到子模块的原因,建议先看上一个博客了解项目结构&#xff1a; springboot 多模块启动报错Field XXX required a bean of type XXX that could not be found. 接下来我们来解决swaggar异常的原因&#xff0c;我们成功启动项目…

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:SideBarContainer)

提供侧边栏可以显示和隐藏的侧边栏容器&#xff0c;通过子组件定义侧边栏和内容区&#xff0c;第一个子组件表示侧边栏&#xff0c;第二个子组件表示内容区。 说明&#xff1a; 该组件从API Version 8开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起…