【JVM】一篇通关JMM内存模型

JMM内存模型

  • 1. 原子性
    • 1-1. 问题分析
    • 1-2. 问题解决
  • 2. 可见性
    • 2-1. 问题分析
    • 2-2. 问题解决
  • 3. 有序性
    • 3-1. 问题分析
    • 3-2. 问题解决
  • 4. CAS与原子性
  • 5. synchronized 优化

1. 原子性

  • 很多人将【java 内存结构】与【java 内存模型】傻傻分不清,【java 内存模型】是 Java Memory Model(JMM)的意思。
  • 简单的说,JMM 定义了一套在多线程读写共享数据时(成员变量、数组)时,对数据的可见性有序性、和原子性的规则和保障

1-1. 问题分析

两个线程对初始值为 0 的静态变量一个做自增,一个做自减,各做 5000 次,结果是 0 吗?

  • 结果可能是正数、负数、零。为什么呢?因为 Java 中对静态变量的自增,自减并不是原子操作。

例如对于 i++ 而言(i 为静态变量),实际会产生如下的 JVM 字节码指令:

getstatic i // 获取静态变量i的值
iconst_1 // 准备常量1
iadd // 加法
putstatic i // 将修改后的值存入静态变量i

而对应 i-- 也是类似:

getstatic i // 获取静态变量i的值
iconst_1 // 准备常量1
isub // 减法
putstatic i // 将修改后的值存入静态变量i

而 Java 的内存模型如下,完成静态变量的自增,自减需要在主存线程内存中进行数据交换:

在这里插入图片描述

1-2. 问题解决

synchronized(同步关键字)

synchronized( 对象 ) {要作为原子操作代码
}

2. 可见性

2-1. 问题分析

先来看一个现象,main 线程对 run 变量的修改对于 t 线程不可见,导致了 t 线程无法停止:

static boolean run = true;
public static void main(String[] args) throws InterruptedException {Thread t = new Thread(()->{while(run){// ....}});t.start();Thread.sleep(1000);run = false; // 线程t不会如预想的停下来
}

为什么会这样?

  1. 初始状态, t 线程刚开始从主内存读取了 run 的值到工作内存。

在这里插入图片描述
2. 因为 t 线程要频繁从主内存中读取 run 的值,JIT 编译器会将 run 的值缓存至自己工作内存中的高速缓存中,减少对主存中 run 的访问,提高效率

在这里插入图片描述
3. 1 秒之后,main 线程修改了 run 的值,并同步至主存,而 t 是从自己工作内存中的高速缓存中读取这个变量的值,结果永远是旧值

在这里插入图片描述

2-2. 问题解决

volatile(易变关键字)

它可以用来修饰成员变量和静态成员变量,他可以避免线程从自己的工作缓存中查找变量的值,必须到主存中获取它的值,线程操作 volatile 变量都是直接操作主存,保证了共享变量的可见性,但不能保证原子性

public class Demo1 {volatile static boolean run = true;public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {while (run) {
// ....}});t.start();Thread.sleep(1000);run = false; // 线程t不会如预想的停下来}}

注意:
synchronized 语句块既可以保证代码块的原子性,也同时保证代码块内变量的可见性。但 缺点是synchronized是属于重量级操作,性能相对更低
如果在前面示例的死循环中加入 System.out.println() 会发现即使不加 volatile 修饰符,线程 t 也 能正确看到对 run 变量的修改了,想一想为什么?

进入println源码:

public void println(int x) {synchronized (this) {print(x);newLine();}
}

可以看出加了synchronized,保证了每次run变量都会从主存中获取

3. 有序性

3-1. 问题分析

看下面一个栗子:

int num = 0;
boolean ready = false;
// 线程1 执行此方法
public void actor1(I_Result r) {if(ready) {r.r1 = num + num;} else {r.r1 = 1;}
}
// 线程2 执行此方法
public void actor2(I_Result r) {num = 2;ready = true;
}

看到这里可能聪明的小伙伴会想到有下面三种情况:

情况1:线程1 先执行,这时 ready = false,所以进入 else 分支结果为 1

情况2:线程2 先执行 num = 2,但没来得及执行 ready = true,线程1 执行,还是进入 else 分支,结果为1

情况3:线程2 执行到 ready = true,线程1 执行,这回进入 if 分支,结果为 4(因为 num 已经执行过了)

但其实还有可能为0哦!😲

有可能还是:线程 2 执行 ready=true ,切换到线程1 ,进入if分支,相加为0,在切回线程 2 执行 num=2

这种现象就是指令重排

3-2. 问题解决

volatile 修饰的变量,可以禁用指令重排

int num = 0;
volatile boolean ready = false;
// 线程1 执行此方法
public void actor1(I_Result r) {if(ready) {r.r1 = num + num;} else {r.r1 = 1;}
}
// 线程2 执行此方法
public void actor2(I_Result r) {num = 2;ready = true;
}

4. CAS与原子性

5. synchronized 优化

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

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

相关文章

1、aigc图像相关

aigc图像相关 一、Diffusion webui 在autodl上部署一些问题二、lora和kohyass(1)角色模型(2)风格模型(3)dreambooth(4)模型合并(5)Lora加Adetail其他 三、sd …

Zookeeper无法启动,报“Unable to load database on disk”

转载说明:如果您喜欢这篇文章并打算转载它,请私信作者取得授权。感谢您喜爱本文,请文明转载,谢谢。 问题描述: 测试环境部署zookeeper的服务器磁盘满了,引起服务异常,将zookeeper进程杀掉之后&…

Apache-ActiveMQ 反序列化漏洞(CVE-2015-5254)复现

CVE-2016-3088 一、环境搭建 Java:jdk8 影响版本 Apache ActiveMQ < 5.13.0 二、用docker搭建漏洞环境 访问一下web界面 然后进入admin目录登录 账号:admin 密码:admin 三、工具准备 cd /opt wget https://github.com/matthiaskaiser/jmet/releases/download/0.1.0/jmet-0…

灸哥问答:作为初学者如何学习设计模式?

最近开始把几年前关于设计模式的笔记逐步整理出来&#xff0c;并且分享给大家。讲设计模式的书籍、视频比比皆是。这次我针对每一个设计模式分为五个方面进行介绍&#xff1a; 什么是 X 设计模式&#xff1f;&#xff08;是什么&#xff1f;&#xff09;为什么用 X 设计模式&a…

Grafana增加仪表盘

1.Grafana介绍 grafana 是一款采用Go语言编写的开源应用&#xff0c;主要用于大规模指标数据的可视化展现&#xff0c;是网络架构和应用分析中最流行的时序数据展示工具&#xff0c;目前已经支持绝大部分常用的时序数据库。 Grafana下载地址&#xff1a;https://grafana.com/g…

image 标签的 width 和 height 有大作用!!!

image 标签的 width 和 height 有大作用!!! 我们将在这里探讨一个问题, 如果在加载图片之后不影响图片下边文字的移动. 在给出答案之前, 要知道 width 在 2019 年之后 有了更多能力. 也就是浏览器会根据 width 和 height 计算 aspect-ratio. 当然, 为了实现我们的目标, 提前知…

YOLOv8改进 添加可变形注意力机制DAttention

一、Deformable Attention Transformer论文 论文地址&#xff1a;arxiv.org/pdf/2201.00520.pdf 二、Deformable Attention Transformer注意力结构 Deformable Attention Transformer包含可变形注意力机制&#xff0c;允许模型根据输入的内容动态调整注意力权重。在传统的Tra…

【MYSQL】-函数

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法&#x1f384; 如 果 你 …

虚拟化技术和云计算的关系

1、云计算底层就是虚拟化技术。 &#xff08;1&#xff09;常见的虚拟化技术&#xff1a;VMware&#xff08;闭源的&#xff0c;需要收费&#xff09;、XEN、KVM &#xff08;2&#xff09;大部分公司用的虚拟化方案&#xff1a;XEN、KVM 2、虚拟化的历史 &#xff08;1&am…

List常见方法和遍历操作

List集合的特点 有序&#xff1a; 存和取的元素顺序一致有索引&#xff1a;可以通过索引操作元素可重复&#xff1a;存储的元素可以重复 List集合的特有方法 Collection的方法List都继承了List集合因为有索引&#xff0c;所以有了很多操作索引的方法 ublic static void main…

zookeeper之集群搭建

1. 集群角色 zookeeper集群下&#xff0c;有3种角色&#xff0c;分别是领导者(Leader)、跟随着(Follower)、观察者(Observer)。接下来我们分别看一下这三种角色的作用。 领导者(Leader)&#xff1a; 事务请求&#xff08;写操作&#xff09;的唯一调度者和处理者&#xff0c;保…

Java项目:100SpringBoot图书管理系统

博主主页&#xff1a;Java旅途 简介&#xff1a;分享计算机知识、学习路线、系统源码及教程 文末获取源码 一、项目介绍 图书管理系统基于SpringBootMybatis开发&#xff0c;系统分为两种角色&#xff0c;分别是管理员和普通用户。 管理员功能如下&#xff1a; 书籍类别管理…