Java并发内存模型详情
作者:onlythinking 发布时间:2023-06-04 23:50:23
目录
1、Java内存模型
2、硬件内存架构
3、实际执行
3.1 共享对象可见性
3.2 竞争条件
Java
是一门支持多线程执行的语言,要编写正确的并发程序,了解Java内存模型是重要前提。而了解硬件内存模型有助于理解程序的执行。
本文主要整理以下内容
Java内存模型
硬件内存架构
共享对象可见性
竞争条件
1、Java内存模型
Java内存模型最新修订是在Java5
。 JSR-176
罗列了 J2SE5.0
相关发布特性,包含其中的 JSR-133
(JavaTM内存模型与线程规范),java虚拟机遵循此规范。延续至今该内存模型在Java8中依然奏效。
JSR 全称
Java Specification Requests
,意为Java标准化技术规范的正式请求。
Java程序运行在虚拟机上(Jvm)。从逻辑角度看,Jvm内存被划分为线程堆栈和堆。每个线程都拥有自己的堆栈,该线程堆栈存储的数据不对其它线程可见。堆内存用于存储共享数据。
线程堆栈存储方法中所有局部变量,包含原始类型(boolean
,byte
,short
,char
,int
,long
, float
,double
)和对象引用。
堆存储需要共享对象和静态变量。
注意:对象不一定都会存储到堆内存。看下面例子,假如果Object对象不需要被其它线程共享,编译器会执行堆分配转化为栈分配。
解释一下,编译器会根据对象是否逃逸做出优化。优化的其中一项就是堆分配转化为栈分配,目的在于减轻GC压力,提升性能。此优化动作由Jvm参数-XX:+DoEscapeAnalysi 进行控制。Java8 默认开启。
测试:通过开启或关闭 -XX:+PrintGC -XX:-DoEscapeAnalysis
观察是否执行GC来判断对象存储位置。
public static void main(String[] args){
for(int i = 0; i < 10000000; i++){
createObj();
}
}
public static void createObj(){
new Object();
}
2、硬件内存架构
如下图,现代计算机通常都装有2个或者更多的CPU
,CPU
又可以是多核。一个CPU
包含一组寄存器,每个CPU
具有一个高速缓存,而高速缓存又分为L1,L2,L3,L4 不同层级缓存。
RAM
为主存储也就是我们说的计算机内存,所有CPU都可以读取主存储。
当CPU
读取主存储数据时,它会将部分主存储数据读入CPU高速缓存中,又将缓存的中一部分读入寄存器执行,操作结束后,将值从寄存器刷新到高速缓存中,高速缓存在特定的时刻将数据统一刷新到内存中。
3、实际执行
事实上,上面阐述的Java
堆栈内存模型是为了理解抽象出来的。实际执行就像下图一样,线程栈和堆的数据可能分散到硬件不同的存储区域。数据分散在不同区域会带来以下两个主要问题。
3.1 共享对象可见性
下面场景两个线程同时操作对象obj.count
,其中一个线程对obj.count
进行更新,但是对其它线程不可见。
线程A操作obj时,先从主存里拷贝一个数据副本到CPU高速缓存,又到寄存器,然后修改obj.count=2
后刷新到CPU高速缓存,但是数据暂未同步到主存。以此同时线程B也操作obj
,拷贝的数据副本仍然为obj.count=1
,这会导致程序结果错误。
解决此问题,可以使用Java volatile
关键字。volatile
可简单理解为跳过CPU高速缓存,让修改结果及时同步到主存,从而保证了其它线程读到最新值。volatile
后期专门介绍。
3.2 竞争条件
另外一种情况假如果多个线程同时更行obj.count
,这时会发生竞争条件。
解决方法,使用Java synchronized
保证线程执行顺序,另外synchronized
包裹中的所有变量都直接从主存读取(跳过CPU高速缓存),并且当线程退出synchronized
后,所有更新的变量将同步到主存。
总结:
本文记录Java内存模型,其中主要内容来源于 Jakob Jenkov 大神博客。
http://tutorials.jenkov.com/java-concurrency/java-memory-model.html
来源:https://www.onlythinking.com/2020/06/08/java%E5%B9%B6%E5%8F%91%E4%B9%8B%E5%86%85%E5%AD%98%E6%A8%A1%E5%9E%8B/
猜你喜欢
- 前言;Apache common-pool对象池介绍:对象生命周期、Config详解、代码说明对象生命周期Config详解maxActive
- 前言EasyCache升级兼容 Springboot2,有个业务系统启动总是会卡住,最后抛出超时异常,如下:java.util.concur
- 1.说明如果你也在用Mybatis,建议尝试该分页插件,这个一定是最方便使用的分页插件。该插件目前支持Oracle,Mysql,MariaD
- 1. 二叉树的顺序存储1.1 存储方式使用数组保存二叉树结构,方式即将二叉树用 层序遍历 方式放入数组中。一般只适合表示完全二叉树,这种方式
- JavaMail API中定义了一个java.mail.Transport类,它专门用于执行邮件发送任务,这个类的实例对象封装了某种邮件发送
- 当我们对一些需要用到的资料进行整理时,会发现文件的内存占用很大,不过是下载或者存储,都不是很方便,这时候我们会想到把文件变成zip格式,即进
- 1.以G71列车为例,首先对车次站台进行占位编码(从1开始到最后一站递加)对以上占位简单描述以下:G71总共18个站点那么我们的单个座位的座
- 一、简介Spring Cache是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能。Spring Cache提供
- 最近在开发项目的时候遇到一个问题,在myecilpes上使用googlede gson读取项目中的json文件成功,然后把项目发布到tomc
- 先引用using System.Runtime.InteropServices; 的命名空间, 然后在合适的位置加上如下代码就OK。。注意:
- Springboot上传文件时提示405问题描述:上传文件时请求不通,状态码返回405,如下图: 问题分析:405 Method
- Java 常量池的实例详解Java的常量池中包含了类、接口、方法、字符串等一系列常量值。常量池在编译期间就已经确定,并保存在*.class文
- java Mybatis存进时间戳封装了一个实体类,里面有个字段 Integer createTime。要利用这个实体类将一个时间戳存进数据
- 一个非常简单的登录权限 * ,具体代码如下:以下代码是继承OncePerRequestFilter实现登录过滤的代码:/** * * @
- 本文实例为大家分享了C语言自定义扫雷游戏的具体代码,供大家参考,具体内容如下实现过程对于用C语言实现扫雷游戏得实现,可将游戏过程分为两个板块
- 职责链模式简介及UML职责链也叫责任链,他是一种行为型模式,它为请求创建了一个接收请求者对象的链,并将请求沿着这条链传递到目标对象去处理。该
- 1.位置:如下:如果没有则在相同位置新建一个2. 添加端口号,修改:如图server.port=8080启动项目就会发现端口号已经成为你刚才
- 实现功能权限校验的功能有多种方法,其一使用 * 拦截请求,其二是使用AOP抛异常。 首先用 * 实现未登录时跳转到登录界面的功能。注意这里没
- 我们在代码中经常使用using保障非托管资源的释放 static void Main(string[] args){
- 本文实例讲述了Java基于IO流读取文件的方法。分享给大家供大家参考,具体如下:public static void readFile(){