JMM初学

文章目录

  • 1,线程间的同步和通信
    • 1.1, 共享内存并发模型 (Shared Memory Model)
      • 线程通信机制
      • 线程同步机制
      • 特点
    • 1.2, 消息传递并发模型 (Message Passing Model)
      • 线程通信机制
      • 线程同步机制
      • 特点
    • 适用场景对比
  • 2,Java内存模型JMM
    • 2.0,Java内存模型的基础
      • (1)内存屏障
      • (2)happens before
    • 2.1,内存可见性
      • (1)为什么会出现内存可见性问题?
      • (2)内存可见性的发生过程
      • (3)JMM如何保证内存可见性
    • 2.2,JMM与重排序
      • (1)指令重排序的类型
      • (2)JMM如何限制重排序
    • 2.3顺序一致性模型
      • (1)核心定义
      • (2)同步程序的顺序一致性效果

  • 根据 JMM 的规定,线程对共享变量的所有操作都必须在自己的本地内存中进行,不能直接从主存中读取
  • Java 运行时内存区域和JMM
    • Java 运行时内存区域描述的是在 JVM 运行时,如何将内存划分为不同的区域,并且每个区域的功能和工作机制。
    • Java 内存模型 (JMM) 主要针对的是多线程环境下,如何在主内存与工作内存之间安全地执行操作。它涵盖的主题包括变量的可见性、指令重排、原子操作等,旨在解决由于多线程并发编程带来的一些问题。(可见性,有序性,原子性)
  • 指令重排是为了提高 CPU 性能,但是可能会导致一些问题,比如多线程环境下的内存可见性问题。

1,线程间的同步和通信

并发编程的线程之间存在两个问题:

  • 线程间如何通信?即:线程之间以何种机制来交换信息

  • 线程间如何同步?即:线程以何种机制来控制不同线程间发生的相对顺序

有两种并发模型可以解决这两个问题:

  • 消息传递并发模型
  • 共享内存并发模型

1.1, 共享内存并发模型 (Shared Memory Model)

Java主要采用这种模型

线程通信机制

  • 通过共享内存进行通信
  • 线程之间共享程序的公共状态(变量、对象等)
  • 线程通过读写共享内存中的变量来隐式通信

线程同步机制

  • 使用显式同步原语控制执行顺序
  • 主要同步手段:
    • 锁(synchronized, Lock)
    • volatile变量
    • 原子变量(AtomicInteger等)
    • 内存屏障

特点

  • 通信是隐式的(通过内存访问)
  • 需要程序员显式控制同步
  • 容易出现竞态条件、死锁等问题

1.2, 消息传递并发模型 (Message Passing Model)

如Go语言的channel、Actor模型

线程通信机制

  • 通过发送和接收消息进行显式通信
  • 线程/进程间没有共享状态
  • 消息通道是唯一的通信媒介

线程同步机制

  • 通信本身就是同步的(发送和接收操作)
  • 常见实现方式:
    • 同步消息传递(发送者阻塞直到消息被接收)
    • 异步消息传递+消息队列
    • CSP(Communicating Sequential Processes)模型

特点

  • 通信是显式的(明确的send/receive操作)
  • 同步内建于通信机制中
  • 避免了共享内存带来的许多问题

适用场景对比

场景推荐模型原因
分布式系统消息传递天然适合网络通信
单机高并发共享内存性能更高
简单并发任务消息传递更易实现和维护
复杂数据共享共享内存更高效的数据访问
容错系统消息传递更好的隔离性和恢复能力
实时系统共享内存更低延迟

2,Java内存模型JMM

Java内存模型(Java Memory Model, JMM)是Java虚拟机规范中定义的一种内存访问规范,它是一种抽象概念,包含缓存、写缓冲区、寄存器等。它规定了多线程环境下如何正确地访问共享变量,以及线程之间如何通过内存进行通信。即解决上述的“线程间如何通信”和“线程间如何同步两个问题”。保证多线程环境下的可见性、有序性和原子性。

JMM解决的三大问题

问题类型描述JMM解决方案
可见性一个线程修改共享变量后其他线程立即可见volatile、synchronized、final
有序性指令执行顺序与代码顺序一致happens-before、内存屏障
原子性操作不可中断synchronized、原子类

2.0,Java内存模型的基础

(1)内存屏障

屏障类型作用
LoadLoad禁止 Load1Load2 重排序
StoreStore禁止 Store1Store2 重排序
LoadStore禁止 Load 和后续 Store 重排序
StoreLoad禁止 Store 和后续 Load 重排序

eg:LoadLoad屏障

确保 Load1 先于 Load2 执行,防止读操作重排序。

StoreLoad 屏障(全能屏障)

  • 作用
    • 禁止 Store 和后续 Load 重排序。
    • 强制刷新所有写操作到主内存,并 使其他 CPU 缓存失效
  • 开销最大,但能保证最强的内存一致性。‘

(2)happens before

Happens-Before 是 Java 内存模型(JMM)的核心概念,它定义了多线程环境下操作之间的可见性保证执行顺序约束,使开发者能够在不深入理解底层内存屏障的情况下编写正确的并发程序。

Happens-Before 描述的是两个操作之间的偏序关系

  • 如果操作 A happens-before 操作 B,那么:
    • A 的执行结果对 B 可见
    • A 的代码顺序在 B 之前

📌 注意:Happens-Before 并不一定代表时间上的先后,而是可见性保证

happens-before的六大规则

  • 程序顺序规则同一线程中的操作,按照代码顺序 happens-before。
  • 监视器锁规则解锁(unlock) happens-before 后续的加锁(lock)
  • volatile 变量规则volatile 写 happens-before 后续的 volatile 读
  • 线程启动规则Thread.start() happens-before 该线程的所有操作
  • 线程终止规则线程的所有操作 happens-before 其他线程检测到它终止(如 t.join() )。
  • 传递性规则:如果 A happens-before B,且 B happens-before C,则 A happens-before C

2.1,内存可见性

内存可见性(Memory Visibility)是多线程编程中的一个核心概念,指的是当一个线程修改了共享变量的值后,其他线程能否立即看到这个修改。如果修改后的值不能及时被其他线程观察到,就会导致内存可见性问题,从而引发程序逻辑错误。

什么是共享变量共享变量是指在多线程环境下可以被多个线程共同访问和修改的变量。对于每一个线程来说,栈都是私有的,而堆是共有的。也就是说,在栈中的变量(局部变量、方法定义的参数、异常处理的参数)不会在线程之间共享,也就不会有内存可见性的问题,也不受内存模型的影响。而在堆中的变量是共享的,一般称之为共享变量。所以,内存可见性针对的是堆中的共享变量。

(1)为什么会出现内存可见性问题?

现代计算机和 JVM 为了提高性能,会采用以下优化策略,导致内存可见性问题:

(1) CPU 缓存架构

  • CPU 不会直接读写主内存(RAM),而是通过**多级缓存(L1/L2/L3 Cache)**来提高访问速度。
  • 每个 CPU 核心有自己的缓存,线程运行时可能只更新自己的缓存,而不会立即同步到主内存。
  • 因此,一个线程的修改可能对其他线程不可见

(2) 指令重排序(Reordering)

  • 编译器优化:JIT 编译器可能会调整指令顺序以提高性能。

  • CPU 乱序执行:CPU 可能会改变指令的执行顺序(只要不影响单线程语义)。

  • 这可能导致线程 A 的修改操作被延迟或乱序执行,导致线程 B 看到的数据不一致。

    public class ReorderingProblem {
    private static int x = 0;
    private static int y = 0;
    private static boolean ready = false;

    public static void main(String[] args) {Thread writer = new Thread(() -> {x = 1;y = 2;ready = true;  // 可能被重排序到前面});Thread reader = new Thread(() -> {while (!ready);  // 等待ready=trueSystem.out.println("x=" + x + ", y=" + y);  // 可能输出x=0, y=2});writer.start();reader.start();
    }
    

    }

由于指令重排序问题,可能会ready = true 可能先于 x = 1 执行,导致 reader 线程看到 x=0,但 y=2

(3) 工作内存(Working Memory)抽象

  • JMM(Java 内存模型)规定,每个线程有自己的工作内存(可以理解为 CPU 缓存 + 寄存器 + 写缓冲区)。
  • 线程操作共享变量时,先在工作内存中修改,再同步回主内存,这可能导致其他线程看不到最新值。

(2)内存可见性的发生过程

在这里插入图片描述

从图中可以看出:

  1. 所有的共享变量都存在主存中。
  2. 每个线程都保存了一份该线程使用到的共享变量的副本。
  3. 如果线程 A 与线程 B 之间要通信的话,必须经历下面 2 个步骤:
    1. 线程 A 将本地内存 A 中更新过的共享变量刷新到主存中去。
    2. 线程 B 到主存中去读取线程 A 之前已经更新过的共享变量。

所以,线程 A 无法直接访问线程 B 的工作内存,线程间通信必须经过主存。

注意,根据 JMM 的规定,线程对共享变量的所有操作都必须在自己的本地内存中进行,不能直接从主存中读取

所以线程 B 并不是直接去主存中读取共享变量的值,而是先在本地内存 B 中找到这个共享变量,发现这个共享变量已经被更新了,然后本地内存 B 去主存中读取这个共享变量的新值,并拷贝到本地内存 B 中,最后线程 B 再读取本地内存 B 中的新值。

(3)JMM如何保证内存可见性

Java内存模型(JMM)的核心作用之一就是解决"如何知道共享变量被其他线程更新了"这个问题。

JMM 通过控制主存与每个线程的本地内存之间的交互,来提供内存可见性保证

Java 中的 volatile 关键字可以保证多线程操作共享变量的可见性以及禁止指令重排序,synchronized 关键字不仅保证可见性,同时也保证了原子性(互斥性)。

在更底层,JMM 通过内存屏障来实现内存的可见性以及禁止重排序。为了程序员更方便地理解,设计者提出了 happens-before 的概念,它更加简单易懂,从而避免了程序员为了理解内存可见性而去学习复杂的重排序规则,以及这些规则的具体实现方法。

2.2,JMM与重排序

Java内存模型(JMM)的一个重要方面就是管理指令重排序(Reordering),它定义了在多线程环境下哪些重排序是被允许的,哪些是被禁止的。理解这一点对编写正确的并发程序至关重要。

(1)指令重排序的类型

  1. 编译器优化的重排序

编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序

  1. 指令级并行的重排序

现代处理器采用指令级并行技术(ILP)来将多条指令重叠执行

  1. 内存系统的重排序

由于处理器使用缓存和读写缓冲区,使得加载和存储操作看上去可能是在乱序执行

(2)JMM如何限制重排序

JMM通过以下几种机制来限制重排序:

  1. happens-before规则

定义了一系列天然的happens-before关系,在这些关系下禁止重排序:

  • 程序顺序规则
  • 监视器锁规则
  • volatile变量规则
  • 线程启动/终止规则
  • 传递性规则
  1. 内存屏障(Memory Barrier)

JMM在关键位置插入内存屏障指令来禁止特定类型的重排序:

屏障类型作用
LoadLoad禁止Load1与Load2重排序
StoreStore禁止Store1与Store2重排序
LoadStore禁止Load与后续Store重排序
StoreLoad全能屏障,禁止Store与后续Load重排序(开销最大)
  1. 特殊关键字语义
  • volatile:禁止与相邻指令重排序
  • final:保证正确构造后的对象对所有线程可见
  • synchronized:进入/退出时隐含内存屏障

2.3顺序一致性模型

(1)核心定义

顺序一致性模型必须满足两个基本条件:

  1. 程序顺序保留:每个线程内部的操作必须按照该线程的程序代码顺序执行。(不允许重排序)
  2. 全局内存顺序:所有线程看到的整个系统的操作执行顺序必须一致

顺序一致性模型虽然理论上完美,但硬件上难以实现,但Java等语言可以提供近似保证。

(2)同步程序的顺序一致性效果

在并发编程中,通过同步机制可以使程序表现出顺序一致性的内存效果,即使底层硬件和编译器可能进行各种优化。

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

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

相关文章

【动手学MCP从0到1】2.5 MCP中的Context日志输出、进度汇报和服务端调用客户端的大模型项目实现步骤详解

MCP中的Context 1. Context2. 日志输出2.1 服务端2.2 客户端2.2.1 客户端代码调试2.2.2 客户端全部代码 3. 进度汇报3.1 服务端3.2 客户端3.2.1 客户端代码调试3.2.2 客户端全部代码 4. 模型调用4.1 服务端4.2 客户端4.2.1 客户端代码调试4.2.2 客户端全部代码 1. Context Con…

QT自定义资源管理器

使用qt 和 C实现。还在优化中 项目地址:GitHub - Linda1226/FileResourceManager: 自定义资源管理器 有问题可以交流

[华为eNSP] OSPF综合实验

目录 配置流程 画出拓扑图、标注重要接口IP 配置客户端IP 配置服务端IP 配置服务器服务 配置路由器基本信息:名称和接口IP 配置路由器ospf协议 测试结果 通过配置OSPF路由协议,实现跨多路由器的网络互通,并验证终端设备的访问能力。 …

如何把本地服务器变成公网服务器?内网ip网址转换到外网连接访问

​ 内网IP只能在本地内部网络连接访问,当本地搭建服务器部署好相关网站或应用后,在局域网内可以通过内网IP访问,但在外网是无法直接访问异地内网IP端口应用的,只有公网IP和域名才能实现互联网上的访问。那么需要如何把本地服务器变…

Linux-文件管理及归档压缩

1.根下的目录作用说明: /:Linux系统中所有的文件都在根下/bin:(二进制命令目录)存放常用的用户命令/boot:系统启动时的引导文件(内核的引导配置文件,grub配置文件,内核配置文件) 例…

从零开始的python学习(七)P95+P96+P97+P98+P99+P100+P101

本文章记录观看B站python教程学习笔记和实践感悟,视频链接:【花了2万多买的Python教程全套,现在分享给大家,入门到精通(Python全栈开发教程)】 https://www.bilibili.com/video/BV1wD4y1o7AS/?p6&share_sourcecopy_web&v…

Linux 查找特定字符详细讲解

CentOS 7 中使用 grep 查找特定字符详细笔记​ 一、grep 命令概述​ grep 全称为 Global Regular Expression Print,即全局正则表达式打印,是 CentOS 7 系统中用于文本搜索的核心工具。它基于正则表达式或固定字符串,在文件、标准输入流中进…

uniappx插件nutpi-idcard 开发与使用指南(适配鸿蒙)

uniappx插件nutpi-idcard 开发与使用指南(适配鸿蒙) 前言 nutpi-idcard 是一个基于 UTS (uni-app TypeScript Syntax) 开发的 uni-app 插件适配鸿蒙,主要用于解析身份证号码,提取其中的关键信息,如地区、出生日期、性…

Grafana-ECharts应用讲解(玫瑰图示例)

工具: MySQL 数据库 MySQL Workbench 数据库管理工具(方便编辑数据) Grafana v11.5.2 Business Charts 6.6(原 Echarts插件) 安装 安装 MySQL社区版安装 MySQL Workbench安装 Grafana在 Grafana 插件中搜索 Business Charts 进行安装以上安装步骤网上教程很多,自行搜…

React状态管理Context API + useReducer

在 React 中,Context API useReducer 是一种轻量级的状态管理方案,适合中小型应用或需要跨组件共享复杂状态的场景。它避免了 Redux 的繁琐配置,同时提供了清晰的状态更新逻辑。 1. 基本使用步骤 (1) 定义 Reducer 类似于 Redux 的 reduce…

3 个优质的终端 GitHub 开源工具

1、Oh My Zsh Oh My Zsh 是一个帮助你管理和美化 zsh 终端的开源工具。它让你的终端更炫酷、更高效。安装后,你可以快速使用各种插件和主题,比如常见的 git 命令简化、支持多种编程语言工具等,每次打开终端都会有惊喜。无论你是开发者还是普…

无人机巡检智能边缘计算终端技术方案‌‌——基于EFISH-SCB-RK3588工控机/SAIL-RK3588核心板的国产化替代方案‌

一、方案核心价值‌ ‌实时AI处理‌:6TOPS NPU实现无人机影像的实时缺陷检测(延迟<50ms)‌全国产化‌:芯片、操作系统、算法工具链100%自主可控‌极端环境适配‌:-40℃~85℃稳定运行,IP65防护等…

SpringAI 1.0.0 正式版——利用Redis存储会话(ChatMemory)

官方文档:Chat Memory :: Spring AI Reference 1. 引言 SpringAI 1.0.0 改动了很多地方,本文根据官方的InMemoryChatMemoryRepository实现了自定义的RedisChatMemoryRepository,并使用MessageWindowChatMemory创建ChatMemory 2. 实现 2.1.…

RFC8489-STUN

0. 学习参考 RFC5389 中文翻译 中文RFC RFC文档 RFC翻译 RFC中文版 RFC 5389:NAT 的会话遍历实用程序 (STUN) --- RFC 5389: Session Traversal Utilities for NAT (STUN) 1. RFC 3489的演变 自 RFC 3489 发布以来的经验发现,…

开始在本地部署自己的 Gitea 服务器

0.简介 在软件开发和团队协作中,代码管理是至关重要的环节。笔者一直使用gitblit管理自己的仓库。然鹅,这个软件已经很久没有更新了。经过多方考察,发现Gitea 是一款轻量级的开源代码托管平台,具有易于部署、资源占用少、功能丰富…

Xsens-AAA工作室品质,为动画师准备

每一帧都讲述着一个故事,当动作真实呈现时,故事便鲜活起来。我们打造并改进了 Xsens Animate,助力专业人士突破数字动画的界限。 通过升级后的 Xsens Animate,您可以获得女性和男性解剖模型以及更精确的运动引擎,从一…

嵌入(Embedding)技术的实现原理与应用场景解析

嵌入(Embedding)技术的实现原理与应用场景解析 引言:从One-Hot到语义空间 在自然语言处理的演进历程中,嵌入技术(Embedding)的诞生标志着一个重要转折点——它让离散的符号表示突破了维度诅咒&#xff0c…

金仓数据库征文-金仓KES数据同步优化实践:逻辑解码与增量同步

目录 一.同步场景与方案选型 二.什么是KES 三.同步环境配置 1.前置条件验证 2.逻辑解码配置 四.同步实施与问题排查 1.结构映射规则 2.增量数据捕获 3.数据一致性校验 五.性能调优实践 1.同步线程优化 2.批量提交优化 3.资源监控指标 六.典型场景解决方案 1.双向…

开源语义分割工具箱mmsegmentation基于Lovedata数据集训练模型

开源语义分割工具箱mmsegmentation安装环境 文章目录 1、下载数据集2、整理数据集3、下载预训练模型4、测试5、训练模型参考官方数据处理步骤 https://github.com/open-mmlab/mmsegmentation/blob/main/docs/zh_cn/user_guides/2_dataset_prepare.md#loveda 数据集类别标签:…

Python概率统计可视化——概率分布、假设检验与分子运动模型

Python概率统计可视化——概率分布、假设检验与分子运动模型 前言 概率统计作为描述不确定性和随机现象的数学工具,广泛应用于物理学、生物学、经济学等领域。然而,抽象的概率分布和统计推断过程往往难以直观理解。可视化技术通过将概率密度、假设检验逻…