JMM初学

news2025/6/7 8:30:47

文章目录

  • 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=true
            System.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.coloradmin.cn/o/2402671.html

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

相关文章

构建云原生安全治理体系:挑战、策略与实践路径

📝个人主页🌹:一ge科研小菜鸡-CSDN博客 🌹🌹期待您的关注 🌹🌹 一、引言:从传统安全走向“云原生安全” 随着企业 IT 架构从传统单体系统向容器化、微服务和云原生平台转型&#xf…

vcs仿真产生fsdb波形的两种方式

目录 方法一: 使用verilog自带的系统函数 方法二: 使用UCLI command 2.1 需要了解什么是vcs的ucli,怎么使用ucli? 2.2 使用ucli dump波形的方法 使用vcs仿真产生fsdb波形有两种方式,本文参考《vcs user guide 20…

Go语言底层(三): sync 锁 与 对象池

1. 背景 在并发编程中,正确地管理共享资源是构建高性能程序的关键。Go 语言标准库中的 sync 包提供了一组基础而强大的并发原语,用于实现安全的协程间同步与资源控制。本文将简要介绍 sync 包中常用的类型和方法: sync 锁 与 对象池,帮助开发…

2025年06月06日Github流行趋势

项目名称:agent-zero 项目地址url:https://github.com/frdel/agent-zero项目语言:Python历史star数:8958今日star数:324项目维护者:frdel, 3clyp50, linuztx, evrardt, Jbollenbacher项目简介:A…

动态规划 熟悉30题 ---上

本来是要写那个二维动态规划嘛,但是我今天在问题时候,一个大佬就把他初一时候教练让他练dp的30题发出来了(初一,啊虽然知道计算机这一专业,很多人从小就学了,但是我每次看到一些大佬从小学还是会很羡慕吧或…

Linux系统:ELF文件的定义与加载以及动静态链接

本节重点 ELF文件的概念与结构可执行文件,目标文件ELF格式的区别ELF文件的形成过程ELF文件的加载动态链接与静态链接动态库的编址与方法调用 一、ELF文件的概念与结构 1.1 文件概述 ELF(Executable and Linkable Format)即“可执行与可链…

【国产化适配】如何选择高效合规的安全数据交换系统?

一、安全数据交换系统的核心价值与国产化需求 在数字化转型浪潮中,企业数据流动的频率与规模呈指数级增长,跨网文件传输已成为日常运营的刚需,所以安全数据交换系统也是企业必备的工具。然而,数据泄露事件频发、行业合规要求趋严…

简化复杂系统的优雅之道:深入解析 Java 外观模式

一、外观模式的本质与核心价值 在软件开发的世界里,我们经常会遇到这样的场景:一个复杂的子系统由多个相互协作的类组成,这些类之间可能存在错综复杂的依赖关系和交互逻辑。当外部客户端需要使用这个子系统时,往往需要了解多个类…

设计模式杂谈-模板设计模式

在进入正题之前,先引入这样一个场景: 程序员A现在接到这样一个需求:这个需求有10个接口,这些接口都需要接收前端的传参,以及给前端返回业务状态信息。出于数据保密的要求,不管是前端传参还是最终参数返回都…

C#入门学习笔记 #6(字段、属性、索引器、常量)

欢迎进入这篇文章,文章内容为学习C#过程中做的笔记,可能有些内容的逻辑衔接不是很连贯,但还是决定分享出来,由衷的希望可以帮助到你。 笔记内容会持续更新~~ 将这四种成语放在一起讲是因为这四种成员都是用来表达数据的。 字段…

广目软件GM DC Monitor

广目(北京)软件有限公司成立于2024年,技术和研发团队均来自于一家具有近10年监控系统研发的企业。广目的技术团队一共实施了9家政府单位、1家股份制银行、1家芯片制造企业的数据中心监控预警项目。这11家政企单位由2家正部级、1家副部级、6家…

每日八股文6.6

每日八股-6.6 Mysql1.怎么查看一条sql语句是否走了索引?2.能说说 MySQL 事务都有哪些关键特性吗?3.MySQL 是如何保证事务的原子性的?4.MySQL 是如何保证事务的隔离性的?5.能简单介绍一下 MVCC 吗?或者说,你…

PostgreSQL17 编译安装+相关问题解决

更新时间:2025.6.6,当前最新稳定版本17.5,演示的是17.5,最新测试版本18beta1 演示系统:debian12 很多时候,只有编译安装才能用上最新的软件版本或指定的版本。这也是编译安装的意义。 一、编译安装 &…

React 第五十六节 Router 中useSubmit的使用详解及注意事项

前言 useSubmit 是 React Router v6.4 引入的强大钩子&#xff0c;用于以编程方式提交表单数据。 它提供了对表单提交过程的精细控制&#xff0c;特别适合需要自定义提交行为或非标准表单场景的应用。 一、useSubmit 核心用途 编程式表单提交&#xff1a;不依赖 <form>…

华为云学堂-云原生开发者认证课程列表

华为云学堂-云原生认证 云原生开发者认证的前5个课程

理解网络协议

1.查看网络配置 : ipconfig 2. ip地址 : ipv4(4字节, 32bit), ipv6, 用来标识主机的网络地址 3.端口号(0~65535) : 用来标识主机上的某个进程, 1 ~ 1024 知名端口号, 如果是服务端的话需要提供一个特定的端口号, 客户端的话是随机分配一个端口号 4.协议 : 简单来说就是接收数据…

全球知名具身智能/AI机器人实验室介绍之AI FACTORY基于慕尼黑工业大学

全球知名具身智能/AI机器人实验室介绍之AI FACTORY基于慕尼黑工业大学 TUM AI FACTORY&#xff0c;即KI.FABRIK&#xff0c;是德国慕尼黑工业大学&#xff08;TUM&#xff09;在巴伐利亚州推出的一个旗舰项目&#xff0c;旨在打造未来工厂&#xff0c;将传统工厂转变为由人工智…

DASCTF

[DASCTF X 0psu3十一月挑战赛&#xff5c;越艰巨越狂热]EzPenetration Tip:数据库里的邮箱key已更改为管理员密码&#xff0c;拿到后可直接登录 打开靶机&#xff0c;用Wappalyzer分析网站&#xff0c;可以看到管理系统是Wordpress&#xff0c;因此可以尝试用WPSSCAN扫描公开…

ModBus总线协议

一、知识点 1. 什么是Modbus协议&#xff1f; Modbus 是一种工业通信协议&#xff0c;最早由 Modicon 公司在1979年提出&#xff0c;目的是用于 PLC&#xff08;可编程逻辑控制器&#xff09;之间的数据通信。它是主从式通信&#xff0c;即一个主机&#xff08;主设备&#xf…

【计算机网络】非阻塞IO——poll实现多路转接

&#x1f525;个人主页&#x1f525;&#xff1a;孤寂大仙V &#x1f308;收录专栏&#x1f308;&#xff1a;计算机网络 &#x1f339;往期回顾&#x1f339;&#xff1a;【计算机网络】非阻塞IO——select实现多路转接 &#x1f516;流水不争&#xff0c;争的是滔滔不息 一、…