深入解析Java内存模型
一、背景
并发编程本质问题是:CPU、内存以及IO三者之间的速度差异。CPU速度快于内存、内存访问速度又远远快于IO,根据木桶理论,程序性能取决于最慢的操作,即IO操作。这样会出现CPU和内存交互时,CPU性能无法被充分利用,内存与IO交互时,内存性能也存在部分损耗,单方面提升CPU或内存的性能是无效的。为了提升CPU或内存的利用率,需要平衡三者的速度差异,计算机系统结构、操作系统以及编译程序都做出了贡献,主要体现为:
1. 计算机体系结构:为CPU增加了缓存,均衡和内存的速度差异;
2. 操作系统:增加了进程和线程,便于分时复用CPU,均衡CPU与IO设备的速度差异;
3. 编译程序:优化CPU指令的执行顺序,使得缓存被更加合理的利用。
上述方案虽然很大程度上解决了程序的性能问题,但也带来了许多隐藏的并发问题,主要为:可见性问题、原子性问题以及有序性问题。
可见性问题:
定义:一个线程对共享变量的修改,另外一个线程能够立刻看到,即可见性
导致原因:CPU缓存
详情解析:在多CPU时代,每个CPU都有自己的缓存,当多个CPU缓存同一份共享变量的数据时,线程A修改了共享变量,但修改目前只在CPU的缓存生效,其他CPU缓存还未来得及获取新修改的数据,线程B读取共享变量,读取的数据是老数据,存在数据不一致问题。
原子性问题:
定义:一个或多个操作在CPU执行过程中不被中断的特性,即原子性
导致原因:多线程上下文切换
详情解析:多线程底层执行是按照时间片来执行的,进行任务切换时,针对的是单个CPU指令,仅能保证单个CPU指令的原子性。而高级语言的一条语句,往往是由多个CPU指令完成。例如count += 1,至少需要三条指令:
1. 首先,将变量count加载到对应CPU的寄存器中;
2. 其次,在寄存器中执行 +1 操作;
3. 最后,将结果写入到内存中
若是线程A执行完指令1后,切换到线程B来执行 count += 1操作,线程B操作后count为2,但线程A中保存的count值仍是1,导致最后的结果为2,实际上应该为3。
Java中的原子操作有哪些:
1. 除long和double之外的基本类型(int, byte, boolean, short, char, float)的赋值操作;针对long操作,直接拆分成两个32位的写入操作。
2. 所有引用reference的赋值操作;
3. java.concurrent.Atomic.*包中所有类的原子操作。
原子操作 + 原子操作 != 原子操作【原子性对比:】
synchronized:不可中断锁,适合竞争不激烈,可读性好
Lock: 可中断,多样化同步,竞争激烈时能维持常态
Atomic: 竞争激烈时能维持常态,比Lock性能好;只能同步一个值
有序性问题:
定义:有序性是指按照代码的先后顺序来执行,但编译器为了优化性能,可能会改变代码的执行顺序。例如:i = 1; j = 2 变成 j = 2; i = 1
导致原因:编译优化
详情解析:在Java领域存在一个双重检查创建单例对象的场景,创建对象JVM底层分为三个步骤:
1. 分配内存空间;
2. 在内存上初始化对象;
3. 将对象地址赋值给实例变量
此时进行了编译优化,将1,2,3变成了1,3,2。那么就会出现多线程访问时,某些线程获取的对象为null,出现空指针问题。
因此,为了解决出现的可见性、原子性以及有序性问题,Java给出了一套解决方案,即Java Memory Model,简称JMM。
二、Java内存模型
为了解决可见性和有序性,直观上可以理解:禁用缓存和编译优化,但程序的性能就无法保证。理想方案是:开发者按需禁用缓存和编译优化,因此Java做了两个方面的工作:
- 定义一种
抽象计算机模型; - 定义
一系列规则,来保证可见性和有序性。
2.1 定义
抽象计算机模型:JMM定义了线程和主内存之间的抽象关系:线程之间共享的变量存储在主内存中,每个线程有一个私有的本地内存,每个本地内存中存储了共享变量的副本。其中本地内存[工作内存]是一个抽象概念,底层对应着缓存、寄存器以及硬件和编译器优化等。主内存和工作内存之间的规范为:

所有的共享变量都存储于主内存:这里的变量值是实例变量、类变量以及数组,因为堆和方法区是线程共享的。(局部变量属于线程私有,不存在线程安全问题)工作内存:每一个线程有自己的工作内存,工作内存中保留了被多个线程使用的变量的工作副本线程不能直接读写主内存中的变量
① 只能操作自己的工作内存中的变量,
② 然后再同步到主内存中工作内存的屏蔽性:不同线程之间不能直接访问对方工作内存中的变量,线程之间的值传递需要通过主内存来完成。(可见性问题的罪魁祸首)
一系列规则:volatile、synchronized和final三个关键字,以及六项Happens-Before规则。
2.2 规则
Happens-Before规则指的是:前面一个操作结果对后续操作是可见的。下面为详细的规则:
单线程规则:一个线程中的每个操作,happens-before于该线程的任意后续操作;监视器锁规则(synchronized):对一个锁的解锁,happens-before于随后对这个锁的加锁;volatile变量规则:对一个volatile修饰的变量的写,happens-before于随后对这个变量的读;传递性:如果A happens-before B、 B happens-before C、则A happens-before C;线程start启动规则:主线程A启动子线程B后,子线程B能够看到主线程在启动子线程B前的操作结果;线程join()规则: 主线程A等待子线程B完成,当子线程B完成后,主线程能够看到子线程的操作结果。final规则: 通过final修饰变量,告诉编译器着变量是不会发生改变的,可以尽情优化。
三、总结
上述解决了可见性和有序性问题,原子性问题通过互斥锁可以完美解决。
相关文章:
深入解析Java内存模型
一、背景 并发编程本质问题是:CPU、内存以及IO三者之间的速度差异。CPU速度快于内存、内存访问速度又远远快于IO,根据木桶理论,程序性能取决于最慢的操作,即IO操作。这样会出现CPU和内存交互时,CPU性能无法被充分利用…...
python使用国内镜像源
使用格式 格式为:pip install 库名 -i 镜像地址(注意空格的存在) pip install pandas -i https://pypi.tuna.tsinghua.edu.cn/simple 推荐的镜像源: 清华大学(推荐):https://pypi.tuna.tsing…...
【动态规划】代码随想录算法训练营第四十六天 |139.单词拆分,关于多重背包,你该了解这些! ,背包问题总结篇!(待补充)
139.单词拆分 1、题目链接:. - 力扣(LeetCode) 2、文章讲解:代码随想录 3、题目: 给定一个非空字符串 s 和一个包含非空单词的列表 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词…...
WordPress建站入门教程:如何选择和设置固定链接结构?
我们成功搭建好WordPress网站后,发布的文章对应的URL地址默认是使用“日期和名称型”,即是网站域名跟着的是年月日,最后是文章标题,如http://www.yigujin.com/2024/03/06/免费响应式WordPress博客主题JianYue/ 为了让我们的文章U…...
一款好用的AI工具——边界AICHAT(三)
目录 3.23、文档生成PPT演示3.24、AI文档翻译3.25、AI翻译3.26、论文模式3.27、文章批改3.28、文章纠正3.29、写作助手3.30、文言文翻译3.31、日报周报月报生成器3.32、OCR-DOC办公文档识别3.33、AI真人语音合成3.34、录音音频总结3.35、域方模型市场3.36、模型创建3.37、社区交…...
编程示例: 矩阵的多项式计算以javascript语言为例
编程示例: 矩阵的多项式计算以javascript语言为例 国防工业出版社的《矩阵理论》一书中第一章第8个习题 试计算2*A^8-3*A^5A^4A^2-4I A[[1,0,2],[0,-1,1],[0,1,0]] 代码如下 <html> <head> <title> 矩阵乘法 </title> <script srcset.js ><…...
project generator 简单使用
文章目录 1 progen 资源2 使用简介2.1 安装2.2 添加 target(可选)2.3 替换 CMake 模板(可选)2.4 创建 progen 项目 3 总结 1 progen 资源 0)简介:progen(project-generator,项目生成…...
C语言 —— 图形打印
题目1: 思路: 如果我们要打印一个实心正方形,其实就是一个二维数组,i控制行,j控制列,行列不需要控制,arr[i][j]直接打印星号即可。 对于空心正方形,我们只需要控制行和列的条件&…...
Python基础学习(11)常用模块
文章目录 一、time二、random三、os四、sys五、json补充1:JSON字符串补充2:JSON字符串和字典的区别 六、hashlib Python基础学习(1)基本知识 Python基础学习(2)序列类型方法与数据类型转换 Python基础学习(3)进阶字符串(格式化输出) Python基础学习(4)散…...
嵌入式学习37-TCP并发模型
TCP并发模型: 1.TCP多线程模型: 缺点: 1.创建线程会带来 资源开销 2.能够实现的 并发量 比较有限 2.IO模型: 1.阻塞IO: 没有…...
C语言字符函数和字符串函数
前言 今天这篇博客咱们一起来认识一些特殊的函数,在编程的过程中,我们经常要处理字符和字符串,为了方便字符和字符串,C语言提供了一些库函数,让我们一起看看这些函数都有什么功能吧!!࿰…...
Go语言必知必会100问题-22 空切片与nil切片有区别吗?
空切片与nil切片有区别吗? 很多开发人员经常混淆nil切片和空切片,不清楚什么时候使用空切片什么时候使用nil,而有些库函数又对这两者使用进行了区分。下面先来看看它们的定义。 空切片是length为0的切片当切片等于nil时为nil切片 下面是几种不同空切片…...
【C++进阶】C++多态概念详解
C多态概念详解 一,多态概念二,多态的定义2.1 多态构成的条件2.2 什么是虚函数2.3 虚函数的重写2.3.1 虚函数重写的特例2.3.2 override和final 2.4 重载和重写(覆盖)和重定义(隐藏)的区别 三,抽象…...
Python 导入Excel三维坐标数据 生成三维曲面地形图(面) 2、线条平滑曲面但有间隔
环境和包: 环境 python:python-3.12.0-amd64包: matplotlib 3.8.2 pandas 2.1.4 openpyxl 3.1.2 scipy 1.12.0 代码: import pandas as pd import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D from scipy.interpolate import griddata imp…...
前端精准测试调用链路分析
精准测试在评估需求的测试范围时,需要评估一下代码的影响范围,这个范围有两部分:一是需求直接修改的代码;二是修改代码影响到的功能模块。代码影响到的功能一般是通过调用链路分析来实现的,java和kotlin代码可以由java…...
Objective-C blocks 概要
1.block的使用 1.1什么是block? Blocks是C语言的扩充功能:带有自动变量(局部变量)的匿名函数。 “带有自动变量”在Blocks中表现为“截取自动变量" “匿名函数”就是“不带名称的函数” 块,封装了函数调用及调用…...
Linux操作系统-07-Linux安装应用
一、使用rpm安装应用(不推荐) 先下载到本地,以.rpm文件名结尾,下载完成后,再安装 rpm -qa | grep mysql #查询当前系统是否有下载过mysql包 先上传mysql的rpm安装包到linux的opt目录 安装 rpm -ivh …...
DevOps实战:Docker、Kubernetes与Jenkins的完美融合
DevOps与容器化技术:Docker、Kubernetes和Jenkins 引言 在软件开发领域,DevOps文化和容器化技术已经成为当今最热门的话题之一。DevOps的目标是缩短开发和运维之间的距离,提高软件交付的速度和质量。而容器化技术,如Docker和Kub…...
Python面向对象——程序架构
需求 创建图形管理器 -记录多种图形(圆形、矩形.) --提供计算总面积的方法, 要求:增加新图形,不影响图形管理器 测试: 创建图形管理器,存储多个图形对象。 通过图形管理器,调用计算总面积方法 思路 代码 # ------…...
springboot单体项目链路日志跟踪及接口耗时
最近接触一个新的传统项目,在联调过程中,查看日志特别不方便,既无trackId,即无接口耗时,所以写了该博客。话不多说,直接上代码 1、实体类user package com.yk.domain;import lombok.Data;@Data public class User {private Long id;private String username;private St…...
网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...
2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...
自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...
基于TurtleBot3在Gazebo地图实现机器人远程控制
1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...
Monorepo架构: Nx Cloud 扩展能力与缓存加速
借助 Nx Cloud 实现项目协同与加速构建 1 ) 缓存工作原理分析 在了解了本地缓存和远程缓存之后,我们来探究缓存是如何工作的。以计算文件的哈希串为例,若后续运行任务时文件哈希串未变,系统会直接使用对应的输出和制品文件。 2 …...
Linux安全加固:从攻防视角构建系统免疫
Linux安全加固:从攻防视角构建系统免疫 构建坚不可摧的数字堡垒 引言:攻防对抗的新纪元 在日益复杂的网络威胁环境中,Linux系统安全已从被动防御转向主动免疫。2023年全球网络安全报告显示,高级持续性威胁(APT)攻击同比增长65%,平均入侵停留时间缩短至48小时。本章将从…...
qt+vs Generated File下的moc_和ui_文件丢失导致 error LNK2001
qt 5.9.7 vs2013 qt add-in 2.3.2 起因是添加一个新的控件类,直接把源文件拖进VS的项目里,然后VS卡住十秒,然后编译就报一堆 error LNK2001 一看项目的Generated Files下的moc_和ui_文件丢失了一部分,导致编译的时候找不到了。因…...
