当前位置: 首页 > article >正文

Java 内存模型(JMM)深度解析:理解多线程内存可见性问题

Java 内存模型(JMM)深度解析:理解多线程内存可见性问题

在 Java 编程中,多线程的运用能够显著提升程序的执行效率,但与此同时,多线程环境下的一些问题也逐渐凸显。其中,内存可见性问题是一个关键挑战。而深入理解 Java 内存模型(JMM)是解决这一问题的基础。

一、Java 内存模型(JMM)概述

Java 内存模型(Java Memory Model,JMM)是 Java 虚拟机(JVM)中定义的一套规范,它规定了 Java 程序中各个变量(包括实例字段、静态字段和构成数组对象的元素等)的访问方式。在 Java 中,线程之间的通信是通过共享变量进行的,而 JMM 正是围绕这些共享变量在并发过程中的可见性、原子性和有序性展开的。

  1. 内存分区模型

    • JMM 从硬件及操作系统层面为 Java 程序员屏蔽了大量的细节,但它也有自己的内存架构。从概念上来说,JMM 将 Java 内存划分为两个主要部分:主内存(Main Memory)和每个线程的私有工作内存(Working Memory)。所有共享变量都存储在主内存中,而每个线程都有自己独立的工作内存,里面保存了从主内存中复制过来的共享变量副本。
    • 当线程对共享变量进行读写操作时,实际上是先操作工作内存中的副本,然后再由工作内存与主内存进行同步。这种设计使得线程之间的通信变得复杂,因为线程无法直接访问其他线程的工作内存,只能通过主内存进行间接交互。
  2. 与硬件内存架构的关系

    • 现代计算机硬件系统中通常包含多个处理器核心,每个核心都有自己的寄存器和高速缓存。当一个线程在某个处理器核心上运行时,它对数据的读写操作可能会被优先存储在该核心的高速缓存中,而不是立即写回主内存。这就可能导致其他处理器核心中的线程无法及时获取到最新的数据状态,从而引发内存可见性问题。
    • JMM 的设计在一定程度上是对硬件内存架构的一种抽象和模拟。它旨在为 Java 程序员提供一种相对统一的内存访问语义,使得程序员在编写多线程代码时能够更好地理解和预测程序的行为,而不必过多地考虑底层硬件的复杂细节。

二、内存可见性问题的典型场景

  1. 共享变量的更新未及时被其他线程察觉
    • 考虑以下代码示例:
public class VisibilityExample {private static boolean flag = false;public static void main(String[] args) throws InterruptedException {new Thread(() -> {while (!flag) {// 线程 1 在这里循环等待 flag 变为 true}System.out.println("线程 1 发现 flag 变为 true 了!");}).start();Thread.sleep(1000); // 确保线程 1 先启动并进入循环new Thread(() -> {flag = true; // 线程 2 更新 flag 的值System.out.println("线程 2 已将 flag 设置为 true!");}).start();}
}
 * 在这个例子中,我们创建了两个线程。线程 1 在一个循环中不断地检查 flag 变量的值,而线程 2 在启动后将 flag 设置为 true。按照直觉,线程 1 应该会在某个时刻检测到 flag 的变化并退出循环,输出相应的消息。* 然而,在实际运行中,线程 1 可能会长时间地处于循环状态,无法及时感知到线程 2 对 flag 的更新。这是因为线程 1 的工作内存中缓存了 flag 的初始值(false),它并没有频繁地去主内存中检查 flag 是否发生了变化。这种情况下,就出现了内存可见性问题,线程 2 对共享变量 flag 的更新结果没有及时被线程 1 看到。
  1. 指令重排序导致的可见性问题
    • 现代的编译器和处理器为了优化程序性能,常常会对指令进行重排序。这可能会使程序的执行顺序与代码的书写顺序不一致,从而引发内存可见性问题。
    • 例如:
public class ReorderExample {private static int x = 0;private static int y = 0;private static int a = 0;private static int b = 0;public static void main(String[] args) throws InterruptedException {Thread thread1 = new Thread(() -> {a = 1; // 操作 1x = b; // 操作 2});Thread thread2 = new Thread(() -> {b = 1; // 操作 3y = a; // 操作 4});thread1.start();thread2.start();thread1.join();thread2.join();System.out.println("x = " + x + ", y = " + y);}
}
 * 在这个例子中,我们期望的是两个线程分别对 a 和 b 进行赋值,然后读取对方线程中变量的值。但由于指令重排序的存在,可能会出现以下情况:线程 1 先执行操作 2(读取 b 的值)然后再执行操作 1(对 a 赋值),而线程 2 也以类似的方式执行操作 4 和操作 3。这样,最终可能会导致 x 和 y 的值都为 0,即使两个线程都已经对 a 和 b 进行了赋值操作。这是因为线程 1 和线程 2 的工作内存中的变量值并没有及时地同步到主内存,使得它们在读取变量时获取到的仍然是旧值。

三、JMM 中解决内存可见性问题的机制

  1. volatile 关键字
    • volatile 是 Java 中一个常用的用于保证内存可见性的关键字。当一个变量被声明为 volatile 修饰时,它会告知 JVM 该变量可能会被多个线程同时访问,因此需要确保对该变量的所有读写操作都直接在主内存中进行,同时禁止指令重排序对 volatile 变量的读写操作进行优化。
    • 例如,将前面第一个示例中的 flag 变量声明为 volatile:
private static volatile boolean flag = false;
 * 这样,线程 1 在每次循环中读取 flag 的值时,都会直接从主内存中获取最新的值,而线程 2 对 flag 的更新操作也会立即写回主内存,从而保证了线程 1 能够及时看到线程 2 对 flag 的修改。通过使用 volatile 关键字,我们有效地解决了之前出现的内存可见性问题。
  1. synchronized 关键字
    • synchronized 是 Java 中另一个用于解决内存可见性和线程安全性问题的重要机制。它通过对共享资源进行加锁的方式,确保在任意时刻只有一个线程能够访问被 synchronized 修饰的代码块或方法。
    • 当一个线程获取了锁并进入 synchronized 代码块时,它会将工作内存中该共享变量的值刷新回主内存,并且在退出 synchronized 代码块时,会从主内存中重新读取共享变量的值,从而保证了不同线程之间共享变量的可见性。
    • 例如:
public class SynchronizedVisibilityExample {private static Object lock = new Object();private static int count = 0;public static void main(String[] args) {Thread thread1 = new Thread(() -> {synchronized (lock) {count++;System.out.println("线程 1 更新 count 为:" + count);}});Thread thread2 = new Thread(() -> {synchronized (lock) {System.out.println("线程 2 看到的 count 为:" + count);}});thread1.start();thread2.start();}
}
 * 在这个例子中,通过使用 synchronized 关键字对 count 变量的访问进行同步,线程 1 对 count 的更新操作会立即对线程 2 可见。当线程 1 进入 synchronized 代码块并修改 count 的值后,线程 2 在获取锁并进入自己的 synchronized 代码块时,能够读取到线程 1 对 count 的最新修改结果。

四、深入理解 happens - before 原则

  1. 概念及作用

    • happens - before(先行发生)原则是 JMM 中用于判断两个操作是否具有可见性的一种规则。如果操作 A happens - before 操作 B,那么操作 A 的结果将对操作 B 可见,并且操作 A 的执行顺序会在操作 B 之前。
    • 通过 happens - before 原则,我们可以更好地理解和预测多线程程序中各个操作之间的内存可见性关系,从而正确地编写和调试并发程序。
  2. happens - before 规则

    • 程序顺序规则:在一个线程中,按照代码的书写顺序,前面的操作 happens - before 后面的操作。
    • 锁定规则:对一个锁的解锁操作 happens - before 后续对同一个锁的加锁操作。
    • volatile 变量规则:对一个 volatile 变量的写操作 happens - before 后面对同一个 volatile 变量的读操作。
    • 传递性规则:如果操作 A happens - before 操作 B,操作 B happens - before 操作 C,那么操作 A happens - before 操作 C。
    • 线程启动规则:Thread 对象的 start() 方法调用 happens - before 对应线程的每一个动作。
    • 线程终止规则:线程中的所有操作 happens - before 对于该线程的终止检测。
    • 线程中断规则:对线程 interrupt() 方法的调用 happens - before 被中断线程的代码检测到中断事件的发生。
    • 对象终结规则:一个对象的初始化完成 happens - before 它的 finalize() 方法的开始。

五、总结

Java 内存模型(JMM)作为 Java 并发编程的核心概念,对理解多线程内存可见性问题具有重要意义。通过深入学习 JMM 的原理,包括内存分区模型、解决内存可见性问题的机制(如 volatile 和 synchronized 关键字)以及 happens - before 原则,我们能够更好地编写高效、可靠的多线程程序,避免因内存可见性问题而导致的程序错误和异常。在实际的 Java 开发过程中,合理地运用这些知识,能够帮助我们更好地应对复杂的并发场景。

希望本文能够帮助读者对 Java 内存模型和多线程内存可见性问题有一个更深入的理解,并在编写多线程代码时更加得心应手。
在这里插入图片描述

相关文章:

Java 内存模型(JMM)深度解析:理解多线程内存可见性问题

Java 内存模型(JMM)深度解析:理解多线程内存可见性问题 在 Java 编程中,多线程的运用能够显著提升程序的执行效率,但与此同时,多线程环境下的一些问题也逐渐凸显。其中,内存可见性问题是一个关…...

转移dp简单数学数论

1.转移dp问题 昨天的练习赛上有一个很好玩的起终点问题,第一时间给出bfs的写法。 但是写到后面发现不行,还得是的dp转移的写法才能完美的解决这道题目。 每个格子可以经过可以不经过,因此它的状态空间是2^(n*m)&…...

【大模型面试每日一题】Day 27:自注意力机制中Q/K/V矩阵的作用与缩放因子原理

【大模型面试每日一题】Day 27:自注意力机制中Q/K/V矩阵的作用与缩放因子原理 📌 题目重现 🌟🌟 面试官:请解释Transformer自注意力机制中Query、Key、Value矩阵的核心作用,并分析为何在计算注意力分数时…...

Ubuntu24.04 LTS安装java8、mysql8.0

在 Ubuntu 24.04 上安装 OpenJDK OpenJDK 包在 Ubuntu 24.04 的默认存储库中随时可用。 打开终端并运行以下 apt 命令: sudo apt update查看是否已经安装java java --version如果未安装会有提示,直接复制命令安装即可,默认版本: sudo apt in…...

动静态库--

目录 一 静态库 1. 创建静态库 2. 使用静态库 2.1 第一种 2.2 第二种 二 动态库 1. 创建动态库 2. 使用动态库 三 静态库 VS 动态库 四 动态库加载 1. 可执行文件加载 2. 动态库加载 一 静态库 Linux静态库:.a结尾 Windows静态库:.lib结尾…...

【检索增强生成(RAG)全解析】从理论到工业级实践

目录 🌟 前言🏗️ 技术背景与价值🩹 当前技术痛点🛠️ 解决方案概述👥 目标读者说明 🧠 一、技术原理剖析📊 核心架构图解💡 核心工作流程🔧 关键技术模块⚖️ 技术选型对…...

git clone时出现无法访问的问题

git clone时出现无法访问的问题 问题: 由于我的git之前设置了代理,然后在这次克隆时又没有打开代理 解决方案: 1、如果不需要代理,直接取消 Git 的代理设置: git config --global --unset http.proxy git config --gl…...

Lesson 22 A glass envelope

Lesson 22 A glass envelope 词汇 dream v. 做梦,梦想 n. 梦 用法:1. have a dream 做梦    2. have a good / sweet dream 做个好梦 [口语晚安]    3. dream about 人/物 梦到……    4. dream that 句子 梦到…… 例句:他昨晚…...

文件系统·linux

目录 磁盘简介 Ext文件系统 块 分区 分组 inode 再谈inode 路径解析 路径缓存 再再看inode 挂载 小知识 磁盘简介 磁盘:一个机械设备,用于储存数据。 未被打开的文件都是存在磁盘上的,被打开的加载到内存中。 扇区:是…...

【Matlab】雷达图/蛛网图

文章目录 一、简介二、安装三、示例四、所有参数说明 一、简介 雷达图(Radar Chart)又称蛛网图(Spider Chart)是一种常见的多维数据可视化手段,能够直观地对比多个指标并揭示其整体分布特征。 雷达图以中心点为原点&…...

【信息系统项目管理师】第24章:法律法规与标准规范 - 27个经典题目及详解

更多内容请见: 备考信息系统项目管理师-专栏介绍和目录 文章目录 【第1题】【第2题】【第3题】【第4题】【第5题】【第6题】【第7题】【第8题】【第9题】【第10题】【第11题】【第12题】【第13题】【第14题】【第15题】【第16题】【第17题】【第18题】【第19题】【第20题】【第…...

使用JProfiler进行Java应用性能分析

文章目录 一、基本概念 二、Windows系统中JProfiler的安装 1、下载exe文件 2、安装JProfiler 三、JProfiler的破解 四、IDEA中配置JProfiler 1、安装JProfiler插件 2、关联本地磁盘中JProfiler软件的执行文件 3、IDEA中启动JProfiler 五、监控本地主机中的Java应用 …...

遥感解译项目Land-Cover-Semantic-Segmentation-PyTorch之一推理模型

文章目录 效果项目下载项目安装安装步骤1、安装环境2、新建虚拟环境和安装依赖测试模型效果效果 项目下载 项目地址 https://github.com/souvikmajumder26/Land-Cover-Semantic-Segmentation-PyTorch 可以直接通过git下载 git clone https://github.com/souvikmajumder26/Lan…...

最大似然估计(Maximum Likelihood Estimation, MLE)详解

一、定义 最大似然估计 是一种参数估计方法,其核心思想是: 选择能使观测数据出现概率最大的参数值作为估计值。 具体来说,假设数据 D x 1 , x 2 , … , x n D{x_1,x_2,…,x_n} Dx1​,x2​,…,xn​独立且服从某个概率分布 P ( x ∣ θ ) P(…...

【单片机】如何产生负电压?

以下是对知乎文章《单片机中常用的负电压是这样产生的!》的解析与总结,结合电路原理、应用场景及讨论要点展开: 一、负电压产生的核心原理 负电压本质是相对于参考地(GND)的电势差为负值,需通过电源或储能…...

Java 8 Stream 流操作全解析

文章目录 **一、Stream 流简介****二、Stream 流核心操作****1. 创建 Stream****2. 中间操作&#xff08;Intermediate Operations&#xff09;****filter(Predicate<T>)&#xff1a;过滤数据****1. 简单条件过滤****2. 多条件组合****3. 过滤对象集合****4. 过滤 null 值…...

java线程中断的艺术

文章目录 引言java中的中断何时触发中断阻塞如何响应中断中断的一些实践基于标识取消任务如何处理阻塞式的中断合理的中断策略时刻保留中断的状态超时任务取消的最优解处理系统层面阻塞IO小结参考引言 我们通过并发编程提升了系统的吞吐量,特定场景下我们希望并发的线程能够及…...

【信息系统项目管理师】一文掌握高项常考题型-项目进度类计算

更多内容请见: 备考信息系统项目管理师-专栏介绍和目录 文章目录 一、进度类计算的基本概念1.1 前导图法1.2 箭线图法1.3 时标网络图1.4 确定依赖关系1.5 提前量与滞后量1.6 关键路径法1.7 总浮动时间1.8 自由浮动时间1.9 关键链法1.10 资源优化技术1.11 进度压缩二、基本公式…...

HarmonyOS 鸿蒙应用开发基础:转换整个PDF文档为图片功能

在许多应用场景中&#xff0c;将PDF文档的每一页转换为单独的图片文件是非常有帮助的。这可以用于文档的分享、扫描文档的电子化存档、或者进行进一步的文字识别处理等。本文将介绍如何使用华为HarmonyOS提供的PDF处理服务将整个PDF文档转换为图片&#xff0c;并将这些图片存放…...

Flask-SQLAlchemy核心概念:模型类与数据库表、类属性与表字段、外键与关系映射

前置阅读&#xff0c;关于Flask-SQLAlchemy支持哪些数据库及基本配置&#xff0c;链接&#xff1a;Flask-SQLAlchemy_数据库配置 摘要 本文以一段典型的 SQLAlchemy 代码示例为引入&#xff0c;阐述以下核心概念&#xff1a; 模型类&#xff08;Model Class&#xff09; ↔ 数…...

刷题 | 牛客 - js中等题-下(更ing)30/54知识点解答

知识点汇总&#xff1a; 数组&#xff1a; Array.prototype.pop()&#xff1a;从数组末尾删除一个元素&#xff0c;并返回这个元素。 Array.prototype.shift()&#xff1a;从数组开头删除一个元素&#xff0c;并返回这个元素。 array.reverse()&#xff1a;将数组元素反转顺…...

RAM(随机存取存储器)的通俗解释及其在路由器中的作用

RAM&#xff08;随机存取存储器&#xff09;的通俗解释及其在路由器中的作用 一、RAM是什么&#xff1f; RAM&#xff08;Random Access Memory&#xff09; 就像餐厅的“临时工作台”&#xff1a; 核心作用&#xff1a;临时存储正在处理的任务&#xff08;如厨师同时处理多道…...

六、【前端启航篇】Vue3 项目初始化与基础布局:搭建美观易用的管理界面骨架

【前端启航篇】Vue3 项目初始化与基础布局&#xff1a;搭建美观易用的管理界面骨架 前言技术选型回顾与准备准备工作第一步&#xff1a;进入前端项目并安装 Element Plus第二步&#xff1a;在 Vue3 项目中引入并配置 Element Plus第三步&#xff1a;设计基础页面布局组件第四步…...

【项目需求分析文档】:在线音乐播放器(Online-Music)

1. 用户管理模块 1.1 注册功能 功能描述 提供注册页面&#xff0c;包含用户名、密码输入框及提交按钮。用户名需唯一性校验&#xff0c;密码使用 BCrypt 加密算法存储。注册成功后自动跳转至登录页面。 1.2 登录功能 功能描述 提供登录页面&#xff0c;包含用户名、密码输入…...

C++ 前缀和数组

一. 一维数组前缀和 1.1. 定义 前缀和算法通过预处理数组&#xff0c;计算从起始位置到每个位置的和&#xff0c;生成一个新的数组&#xff08;前缀和数组&#xff09;。利用该数组&#xff0c;可以快速计算任意区间的和&#xff0c;快速求出数组中某一段连续区间的和。 1.2. …...

PHP 实现通用数组字段过滤函数:灵活去除或保留指定 Key

PHP 实现数组去除或保留指定字段的通用函数详解 一、文章标题 《PHP 实现通用数组字段过滤函数:灵活去除或保留指定 Key》 二、摘要 在实际开发中,我们经常需要对数组进行字段级别的操作,例如从一个数组中删除某些敏感字段(如密码、token),或者只保留特定字段用于接口…...

NACOS2.3.0开启鉴权登录

环境 名称版本nacos2.3.0&#xff08;Linux&#xff09;java java version "17.0.14" 2025-01-21 LTS # # Copyright 1999-2021 Alibaba Group Holding Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use thi…...

细胞冻存的注意事项,细胞冻存试剂有哪些品牌推荐

细胞冻存的原理 细胞冻存的基本原理是利用低温环境抑制细胞的新陈代谢&#xff0c;使细胞进入一种“休眠”状态。在低温条件下&#xff0c;细胞的生物活动几乎停止&#xff0c;从而实现长期保存。然而&#xff0c;细胞在冷冻过程中可能会因为细胞内外水分结冰形成冰晶而受损。…...

快速上手Linux火墙管理

实验网络环境&#xff1a; 主机IP网络f1192.168.42.129/24NATf2&#xff08;双网卡&#xff09; 192.168.42.128/24 192.168.127.20/24 NAT HOST-NOLY f3192.168.127.30/24HOST-ONLY 一、iptables服务 1.启用iptables服务 2.语法格式及常用参数 语法格式&#xff1a;参数&…...

[创业之路-375]:企业战略管理案例分析 - 华为科技巨擘的崛起:重构全球数字化底座的超级生命体

在人类文明从工业时代&#xff08;机械、电气、自动化&#xff09;迈向数字智能&#xff08;硬件、软件、算法、虚拟、智能&#xff09;时代的临界点上&#xff0c;一家中国企业正以令人震撼的姿态重塑全球科技版图。从通信网络的底层架构到智能终端的生态闭环&#xff0c;从芯…...