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/


猜你喜欢
- Java Spring Controller 获取请求参数的几种方法 1、直接把表单的参数写在Controller相应的方法的形参
- Mybatis的日志模块的适配器模式我们在开发中日志是必不可少的一部分,而市场中有很多日志框架供我们使用,mybatis作为一个开源框架需要
- 本文实例讲述了Android编程实现调用相册、相机及拍照后直接裁剪的方法。分享给大家供大家参考,具体如下:package com.cvte.
- 被接受的特性1. Jigsaw 项目;模块化源码Jigsaw项目是为了模块化Java代码、将JRE分成可相互协作的组件,这也是Java 9
- 今天把Android Studio 2.3 更新为了3.0 遇到一个蛋疼的问题如图:格式化完代码后发现不会自动换行了,看着真心不爽。后来发现
- 实际开发中我们需要很多情况需要判断某个activity是否位于栈顶,也许会给新的小伙伴带来困扰,那么直接上代码吧,也没几行/** * *
- 本文实例讲述了Java实现数组转字符串及字符串转数组的方法。分享给大家供大家参考,具体如下:字符串转数组使用Java split() 方法s
- 参考ColorComboBox做修改,并对颜色名做些修正,用于CR MVMixer产品中,聊作备忘~效果图:代码://颜色拾取框using
- 一、引入其实之前一直以为像饿了么或者是美团外卖那种把商品添加到购物车的动画会很难做,但是实际做起来好像并没有想象中的那么难哈哈。布局主要使用
- PhotoView的简介:这是一个图片查看库,实现图片浏览功能,支持pinch(捏合)手势或者点击放大缩小。支持在ViewPager中翻页浏
- 每种编程语言都有自己操作内存中元素的方式,例如在 C 和 C++ 里是通过指针,而在 Java 中则是通过“引用”。在 JDK.1.2 之后
- 原因分析@Anysc注解会开启一个新的线程,主线程的Request和子线程是不共享的,所以获取为null在使用springboot的自定带的
- 最近在做一个资源共享的项目中,采用了Struts2.1.8+Spr
- 这篇文章从系统源代码分析,讲述如何将程序创建的多媒体文件加入系统的媒体库,如何从媒体库删除,以及大多数程序开发者经常遇到的无法添加到媒体库的
- JUnit是Java中最有名的单元测试框架,用于编写和运行可重复的测试,多数Java的开发环境都已经集成了JUnit作为单元测试的工具。好的
- Jackson 是当前用的比较广泛的,用来序列化和反序列化 json 的 Java 的开源框架。Jackson 社 区相对比较活跃,更新速度
- IO的本质IO的作用就是从外部系统读取数据到java程序中,或者把java程序中输出的数据写回到外部系统。这里的外部系统可能是磁盘,网络流等
- 在C#中,@符号不仅可以加在字符串常量之前,使字符串不作转义之用,还可以加在变量名之前,使变量名与关键字不冲突,这种用法称为“逐字标识符”。
- 大家在使用 Intellij IDEA 的时候会经常遇到各种乱码问题,甚是烦扰。栈长也偶尔会用下IDEA,也有一些解决乱码的经验,我给大家总
- SlidingDrawer隐藏屏外的内容,并允许用户通过handle以显示隐藏内容。它可以垂直或水平滑动,它有俩个View组成,其一是可以拖