JMM中工作内存和主内存的关系

Java运行时的数据区域分布:

一、共享区域:

(1)方法区:存储了每个类的信息(包括类的名称、方法信息、字段信息)、静态变量、常量以及编译器编译后的代码等。其中常量池就是在此区域:记录了每一个类或接口的常量池的运行时表示形式,运行期间也可将新的常量放入运行时常量池中,比如String的intern方法。

(2)堆:jvm中最大的一片区域,所有实例对象的内存分配都在这里进行划分。当对象无法在此得到分配空间时,就会OutOfMemory。这部分空间也是Java垃圾收集器管理的主要区域。

二、线程私有:

(1)程序计数器:在JVM中,多线程是通过线程轮流切换来获得CPU执行时间的,因此,在任一具体时刻,一个CPU的内核只会执行一条线程中的指令,因此,为了能够使得每个线程都在线程切换后能够恢复在切换之前的程序执行位置,每个线程都需要有自己独立的程序计数器,并且不能互相被干扰,否则就会影响到程序的正常执行次序。因此,可以这么说,程序计数器是每个线程所私有的。同时,如果线程数量太多,对于cpu来说就需要频繁切换线程,导致cpu占有率上升。

(2)java栈:存放的是一个个的栈帧,每个栈帧对应一个被调用的方法,在栈帧中包括局部变量表(Local Variables)、操作数栈(Operand Stack)、指向当前方法所属的类的运行时常量池(运行时常量池的概念在方法区部分会谈到)的引用(Reference to runtime constant pool)、方法返回地址(Return Address)和一些额外的附加信息。

当线程执行一个方法时,就会随之创建一个对应的栈帧,并将建立的栈帧压栈。当方法执行完毕之后,便会将栈帧出栈。因此可知,线程当前执行的方法所对应的栈帧必定位于Java栈的顶部。当程序设计时调用的栈的深度太深,有可能会导致无法创建栈帧,导致OutOfMermory。

(3)本地栈:类似Java栈,但存放但是native方法的栈帧。

线程/工作内存/主内存 三者的关系

一、每个线程都有一个独立的工作内存,用于存储线程私有的数据

二、Java内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可以访问

三、线程对变量的操作(读取赋值等)必须在工作内存中进行。(线程安全问题的根本原因)

(1)首先要将变量从主内存拷贝的自己的工作内存空间

(2)然后对变量进行操作,操作完成后再将变量写回主内存

(3)不能直接操作主内存中的变量,工作内存中存储着主内存中的变量副本拷贝

(4)因此不同的线程间无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成。

主内存和工作内存

一、主内存是在运行期间所有变量的存放区域,当工作内存是运行期间中某一线程独立私有的内存存放区域

二、线程间无法访问对方的工作内存空间,都是通过主内存交换来实现

三、主内存的变量在工作内存中的值是复制过去的副本,读写完成后刷新主内存,这意味着主内存如果发生了改变,工作内存并无法获得最新的结果

四、多个线程对一个共享变量进行修改时,都是对自己工作内存的副本进行操作,相互不可见。主内存最后得到的结果是不可预知的

new Thread(new Runnable() {@Overridepublic void run() {System.out.println("thread1 is start!");boolean wIsStop = false;while (!isStop) {}System.out.println("thread1 is going to stop!");}}).start();

上面这段代码中,主线程改变isStop的变量后,是无法退出循环的。因为isStop这个变量在线程中从主内存读取到副本后,一直使用的是工作内存的副本。

Synchronized和Volatile

由于线程运行时,对工作内存的变量进行操作时,都是副本。
产生了两个问题:
1.如何使工作内存副本从主内存获取最新的值?
2.如何使其副本是最新避免在写入之前被其他线程修改?

在主线程中改变了isStop=true后
下面这段代码可以退出循环:

new Thread(new Runnable() {@Overridepublic void run() {System.out.println("thread2 is start!");while (!isStop) {synchronized(String.class) {}}System.out.println("thread2 is going to stop!");System.out.println("break count: "  + count);}}).start();

或者是在while循环中加入了System.out.println的语句,也会退出循环。

new Thread(new Runnable() {@Overridepublic void run() {System.out.println("thread2 is start!");while (!isStop) {System.out.println("thread2 stopFlag:" + isStop);}System.out.println("thread2 is going to stop!");System.out.println("break count: "  + count);}}).start();

其实System.out.println的实现中,使用了Synchonized关键字获取输出对象的锁。
一开始以为是Synchronized引起了工作内存从主内存刷新最新的数据,感觉不太合理,后台各种搜索后找到比较靠谱的答案:

jvm有一个锁优化原则那就是 : 粗化锁。如果一系列的连续操作都对同一个对象反复加锁和解锁,甚至加锁操作是出现在循环体中的,那即 使没有线程竞争,频繁地进行互斥同步操作也会导致不必要的性能损耗。 如果虚拟机探测到有这样一串零碎的操作都对同一个对象加锁,将会把加锁同步的范围扩展(膨胀)到整个操作序列的外部(由多次加锁编程只加锁一次)。


------>更新<--------
上面粗化锁的解释应该也不准确,锁的对象并不是变量isStop,并不能将isStop加上一个线程可见的属性。
在synchronized锁操作中,由于是一个耗时操作,jvm会尽力在cpu空闲时间去主内存同步工作内存中变量的值。
------>更新<--------

同样下面的代码,也能退出循环:

new Thread(new Runnable() {@Overridepublic void run() {System.out.println("thread3 is start!");while (!isStop) {try {Thread.sleep(1);}catch (Exception e) {e.printStackTrace();}}System.out.println("thread3 is going to stop!");}}).start();

发现这个循环体里面并没有synchronized的关键字,猜测只能是jvm在线程有空闲的时间尽力的去主存取最新数据。

引入Synchronized后,同步代码块在循环体内,synchronized保证原子性和有序性,可见性。

为了保证不同线程间可以获得同一个共享变量,Volatile也可以做到。

volatile变量在每次被线程访问时,都强迫从主内存中读该变量的值,而当该变量发生变化时,又会强迫将最新的值刷新到主内存,任何时刻,不同的线程总是能够看到该变量的最新值。
上面代码中,对isStop用volatile修饰,也可以退出线程。

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

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

相关文章

使用MyBatis获得包含内嵌对象或对象集合的结果——MyBatis结果集封装(复杂对象,连接查询结果集封装)

最终要获得的SysUser对象 public class SysUser implements Serializable {//普通String属性private String userId;private String userName;private String userPhone;private String userPassword;private String userLastLoginTime;private String userCreateTime;private…

SpringBoot配置MyBatis的sql执行超时时间(mysql)

当某些sql因为不知名原因堵塞时&#xff0c;为了不影响后台服务运行&#xff0c;想要给sql增加执行时间限制&#xff0c;超时后就抛异常&#xff0c;保证后台线程不会因为sql堵塞而堵塞。 方法一 yml全局配置&#xff1a;单数据源可以&#xff0c;多数据源时会失效 方法二 j…

HTTP协议中的302,303状态码

之前也只知道302,303是请求重定向,但是当被问到302,303的具体区别是什么的时候我有点迷,现在就为了加强记忆,来了解下具体情况: 302是http1.0的内容&#xff0c;303是http1.1的内容。301和302本来在规范中是不允许重定向时改变请求方法的&#xff08;将POST改为GET&#xff09…

Java8中Stream为什么要boxed

在介绍boxed前&#xff0c;先看下面一个案例 Java8中的有个生成随机数的Random类&#xff0c;先看下面代码&#xff0c;功能是生成100个随机数。数字在0到100之间。 Random random new Random(); IntStream intStream random.ints(0, 100); intStream.lim…

Spring工具类:FileCopyUtils、StreamUtils

文章目录FileCopyUtilsStreamUtils参考资料Spring 在 org.springframework.util 包提供了很多实用的工具类。今天关心一下Copy 相关的两个&#xff1a; FileCopyUtils FileCopyUtils就是对StreamUtils的copy方法进行了封装&#xff0c;在每次复制完毕后关闭流。&#xff08;因…

Spring自带工具类(断言、ObjectUtils、FileCopyUtils、ResourceUtils、StreamUtils、ReflectionUtils、AopUtils、AopCont)

文章目录断言对象、数组、集合文件、资源、IO 流反射、AOP断言 断言是一个逻辑判断&#xff0c;用于检查不应该发生的情况Assert 关键字在 JDK1.4 中引入&#xff0c;可通过 JVM 参数-enableassertions开启SpringBoot 中提供了 Assert 断言工具类&#xff0c;通常用于数据合法…

Arrays.asList踩坑——引发的Exception in thread “main“ java.lang.UnsupportedOperationException

Exception in thread “main” java.lang.UnsupportedOperationException 如果你尝试修改Arrays.asList方法生产的List&#xff0c;那么就会报这个错误 public static void main(String[] args) {Integer[] arr new Integer[]{7,8,9};List<Integer> list Arrays.asLi…

GIS算法:JAVA拓扑套件JTS

常用可以用于GIS数据处理和空间计算的java包有geotool和jts。 相对来说&#xff0c;geotool功能更全面&#xff0c;还可以用于数据转换、瓦片地图发布、栅格影像分析等&#xff0c;jts只能进行基本的数据处理和空间计算。 但大多数情况下jts就完全够用了。 geotool的官网&am…

Java本地远程服务器debug调试详解

日常我们debug是经常用的&#xff0c;但是本地还好说&#xff0c;远程debug就有点难度&#xff0c;而且有时候必须要在预演&#xff0c;测试环境的服务器去debug&#xff0c;举个例子&#xff0c;需要https&#xff0c;公网&#xff0c;域名之类的&#xff0c;测试服务器这些有…

Linux “ll“ 命令详解

“ls -l” “ls -al” ll 用来查询当前目录下文件及目录的详情 1.第一位文件类型 普通文件 &#xff0c; d 目录文件&#xff0c;I 链接文件&#xff0c;p 管理文件&#xff0c; b 块设备文件&#xff0c; c 字符设备文件&#xff0c; s 套接字文件 2.文件属性 第一部分表示文…

使用BASE64Decoder完成文件与二进制之间互相转化

import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.OutputStreamWriter; import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder;/**\* 使用sun.misc.BASE64Decoder和sun.misc.BASE64Encoder\* 完成文件转化二进…

SpringBoot项目jar发布获取jar包所在目录路径

//第一种File path new File(ResourceUtils.getURL("classpath:").getPath());if(!path.exists()) path new File("");System.out.println(path.getAbsolutePath());//第二种System.out.println(System.getProperty("user.dir"));//第三种Stri…

路径classpath,classpath*,以及file:

./ 当前目录 …/上一层目录 /是根目录 1. classpath : 类路径&#xff0c;指的是编译后的字节码文件存储路径&#xff0c;一般为target目录下的classes目录&#xff08;java项目&#xff09;&#xff0c;在web项目中指的是WEB-INF下的classes目录。实际上&#xff0c;两者其实…

到底什么时候该使用MQ?

一、缘起 一切脱离业务的架构设计与新技术引入都是耍流氓。 引入一个技术之前&#xff0c;首先应该解答的问题是&#xff0c;这个技术解决什么问题。 就像微服务分层架构之前&#xff0c;应该首先回答&#xff0c;为什么要引入微服务&#xff0c;微服务究竟解决什么问题&…

Java中ByteArrayInputStream和ByteArrayOutputStream用法详解

Java中ByteArrayInputStream和ByteArrayOutputStream用法详解 这篇文章主要介绍了Java中ByteArrayInputStream和ByteArrayOutputStream用法详解&#xff0c; ByteArrayInputStream 的内部额外的定义了一个计数器&#xff0c;它被用来跟踪 read() 方法要读取的下一个字节 Byte…

Java中的Base64详解

详解Java中的Base64原理跟用法 简介 ​ Base64编码&#xff0c;是我们程序开发中经常使用到的编码方法。它是一种基于用64个可打印字符来表示二进制数据的表示方法。它通常用作存储、传输一些二进制数据编码方法, 也是MIME&#xff08;多用途互联网邮件扩展&#xff0c;主要用…

Java程序执行Linux命令

java程序中要执行linux命令主要依赖2个类&#xff1a;Process和Runtime 首先看一下Process类&#xff1a; ProcessBuilder.start() 和 Runtime.exec 方法创建一个本机进程&#xff0c;并返回 Process 子类的一个实例&#xff0c; 该实例可用来控制进程并获得相关信息。Proces…

ImageIO类说明

最近的项目中遇到ImageIO&#xff0c;因此记录下这个类的用法 一、ImageIO&#xff1a; 这个类中的方法都是静态方法&#xff0c;可以用来进行简单的图片IO操作 1、读入的三种方法 public static BufferedImage read(File input) File file new File("/Users/xixi/Docum…

java:图像(BufferedImage)色彩空间转换(灰度)暨获取图像矩阵数据byte[](sRGB/gray)

ColorConvertOp java.awt.image包下面有个类java.awt.image.ColorConvertOp,类名直译就是”颜色转换操作”。 顾名思义,它的作用就是将一个色彩空间(color space)的图像转换为另一个色彩空间的图像。有了这个神器我们就能轻易的将一张彩色图你像转换成灰度(gray)或其他色彩空间…

MyBatis-Plus updateById方法更新不了空字符串/null解决方法

最近遇到了Mybatis-Plus updateById()&#xff0c;更新某一个字段为null&#xff0c;却发现没有更新成功。记录一下 一、简介 因为最近在忙项目&#xff0c;好久都没有更新博客&#xff0c;最近在项目中刚好遇到一个问题&#xff0c;就是在使用MyBatis-Plus updateById&#…