首页 > 系统相关 >JVM基础知识(二)Java内存模型

JVM基础知识(二)Java内存模型

时间:2024-09-22 13:51:17浏览次数:9  
标签:Java 基础知识 volatile 内存 JVM JMM 线程 排序 操作

java线程之可见性

volatile不需要加锁, 比synchronized更轻量级, 不会阻塞线程; 从内存可见性角度看, volatile读相当于加锁, volatile写相当于解锁。
synchronized既能保证可见性, 又能保证原子性; volatile只能保证可见性,无法保证原子性。

同步

退出同步块 -> 释放监视器 -> 刷新缓冲区到主内存; 进入同步块 -> 获取监视器 -> 本地处理器缓存失效 -> 重新从主内存加载变量。
线程A与线程B之间如要通信: 首先,线程A把本地内存A中更新过的共享变量刷新到主内存中去。然后,线程B到主内存中去读取线程A之前已更新过的共享变量。

happens-before

从JDK5开始,java使用新的JSR -133内存模型。JSR-133提出了happens-before的概念,通过这个概念来阐述操作之间的内存可见性:

  • 程序顺序规则:一个线程中的每个操作,happens- before 于该线程中的任意后续操作。
  • 监视器锁规则:对一个监视器锁的解锁,happens- before 于随后对这个监视器锁的加锁。
  • volatile变量规则:对一个volatile域的写,happens- before 于任意后续对这个volatile域的读。
  • 传递性:如果A happens- before B,且B happens- before C,那么A happens- before C。

重排序

重排序分为三种: 编译器优化的重排序, 指令级并行的重排序, 内存系统的重排序。

  • 对于编译器重排序,JMM的编译器重排序规则会禁止特定类型的编译器重排序。
  • 对于处理器重排序,JMM的处理器重排序规则会要求java编译器在生成指令序列时,插入特定类型的内存屏障指令,通过内存屏障指令来禁止特定类型的处理器重排序。
    内存屏障的四种分类:
屏障类型 指令示例 说明
LoadLoad Barriers Load1; LoadLoad; Load2 确保Load1数据的装载,之前于Load2及所有后续装载指令的装载。
StoreStore Barriers Store1; StoreStore; Store2 确保Store1数据对其他处理器可见(刷新到内存),之前于Store2及所有后续存储指令的存储。
LoadStore Barriers Load1; LoadStore; Store2 确保Load1数据装载,之前于Store2及所有后续的存储指令刷新到内存。
StoreLoad Barriers Store1; StoreLoad; Load2 确保Store1数据对其他处理器变得可见(指刷新到内存),之前于Load2及所有后续装载指令的装载。StoreLoad Barriers会使该屏障之前的所有内存访问指令(存储和装载指令)完成之后,才执行该屏障之后的内存访问指令。

数据依赖关系(分为三种: 写后读, 写后写, 读后写)

不管怎么重排序(编译器和处理器为了提高并行度),(单线程)程序的执行结果不能被改变。编译器,runtime 和处理器都必须遵守as-if-serial语义。

程序顺序规则:如果A happens- before B,JMM并不要求A一定要在B之前执行。JMM仅仅要求前一个操作(执行的结果)对后一个操作可见,且前一个操作按顺序排在第二个操作之前。如果重排序操作A和操作B后的执行结果与按happens- before顺序执行的结果一致。在这种情况下,JMM会认为这种重排序并不非法,JMM允许这种重排序。

顺序一致性

顺讯一致性内存模型是一个被计算机科学家理想化了的理论参考模型。其有两大特性:

  • 一个线程中的所有操作必须按照程序的顺序来执行。
  • (不管程序是否同步)所有线程都只能看到一个单一的操作执行顺序。在顺序一致性内存模型中,每个操作都必须原子执行且立刻对所有线程可见。

同步程序的顺序一致性效果:

  • 在顺序一致性模型中,所有操作完全按程序的顺序串行执行。而在JMM中,临界区内的代码可以重排序。JMM会在退出监视器和进入监视器这两个关键时间点做一些特别处理,使得线程在这两个时间点具有与顺序一致性模型相同的内存视图。虽然线程A在临界区内做了重排序,但由于监视器的互斥执行的特性,这里的线程B根本无法“观察”到线程A在临界区内的重排序。

JMM不保证未同步程序的执行结果与该程序在顺序一致性模型中的执行结果一致。未同步程序在JMM和顺序一致性模型的执行特性有以下差异:

  • 顺序一致性模型保证单线程内的操作会按程序的顺序执行,而JMM不保证单线程内的操作会按程序的顺序执行(比如同步的多线程程序在临界区内的重排序)。
  • 顺序一致性模型保证所有线程只能看到一致的操作执行顺序,而JMM不保证所有线程能看到一致的操作执行顺序。
  • JMM不保证对64位的long型和double型变量的读/写操作具有原子性,而顺序一致性模型保证对所有的内存读/写操作都具有原子性。

volatile

volatile变量特性:

  • 可见性:对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入。
  • 原子性:对任意单个volatile变量的读/写具有原子性,但类似于volatile++这种复合操作不具有原子性。

volatile写-读的内存语义:

  • 当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存。
  • 当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。

小结: 线程A写一个volatile变量,随后线程B读这个volatile变量,这个过程实质上是线程A通过主内存向线程B发送消息。

为了实现volatile内存语义,JMM会分别限制编译器重排序和处理器重排序, JMM针对编译器制定的volatile重排序规则如下:

  • 当第二个操作是volatile写时,不管第一个操作是什么,都不能重排序。这个规则确保volatile写之前的操作不会被编译器重排序到volatile写之后。
  • 当第一个操作是volatile读时,不管第二个操作是什么,都不能重排序。这个规则确保volatile读之后的操作不会被编译器重排序到volatile读之前。
  • 当第一个操作是volatile写,第二个操作是volatile读时,不能重排序。

对于x86处理器,仅会对写-读操作做重排序。X86不会对读-读,读-写和写-写操作做重排序,因此在x86处理器中会省略掉这三种操作类型对应的内存屏障。在x86中,JMM仅需在volatile写后面插入一个StoreLoad屏障即可正确实现volatile写-读的内存语义 。

锁释放与volatile写有相同的内存语义;锁获取与volatile读有相同的内存语义。
公平锁内存语义的实现:

  • 公平锁在释放锁的最后写volatile变量state;在获取锁时首先读这个volatile变量。根据volatile的happens-before规则,释放锁的线程在写volatile变量之前可见的共享变量,在获取锁的线程读取同一个volatile变量后将立即变的对获取锁的线程可见。

公平锁和非公平锁的区别:

  • 公平锁和非公平锁释放时,最后都要写一个volatile变量state。
  • 公平锁获取时,首先会去读这个volatile变量。非公平锁获取时,首先会用CAS[即java的compareAndSet()方法调用]更新这个volatile变量,这个操作同时具有volatile读和volatile写的内存语义。

标签:Java,基础知识,volatile,内存,JVM,JMM,线程,排序,操作
From: https://blog.51cto.com/u_17015020/12080284

相关文章

  • 大学生HTML期末大作业——HTML+CSS+JavaScript培训机构(画室)
    HTML+CSS+JS【培训机构】网页设计期末课程大作业web前端开发技术web课程设计网页规划与设计......
  • Java入门基础知识点整理大放送,赶紧收藏吧!
    浮点数:float??????4个字节double??8个字节布尔:boolean??1个字节引用类型:字符串String、类class、枚举enum、接口interface3、二进制(1)计算机中的数据都以二进制数据保存。(2)计算机信息的存储单位:位(bit):是计算机存储处理信息的最基本的单位字节(byte):一个字节有8个位组......
  • Java并发(六):ReentrantLock的解锁过程
    可以看到其调用的还是内部类sync的方法,而且可以看到这是一个无返回值的方法并且传入了一个为1的参数release方法可以看到,其调用的是AQS里面的release方法步骤如下先调用tryRelease方法,尝试进行解锁然后判断是否需要唤醒线程返回true,代表释放锁成功tryRelease方法......
  • Java开发八月七号下午笔试 面试
    SpringBoot有两种配置方式,properties和yml,两种配置方式只是格式上不同,功能是一致的,比如properties:server.port=8080对应的yml:server:port:8080就实际开发而言,yml更简洁一些,但是properties出错率更低一些。2、SpringBoot怎么修改启动时的端口号?(1)、在配置文件中修改端口号:......
  • 十四、 软件可靠性基础知识(考点篇)
    1软件可靠性基本概念软件可靠性是软件产品在规定的条件下和规定的时间区间完成规定功能的能力。软件可靠性和硬件可靠性区别(1)复杂性:软件复杂性比硬件高,大部分失效来自于软件失效。(2)物理退化:硬件失效主要是物理退化所致,软件不存在物理退化。(3)唯一性:软件是唯一......
  • 【java面经速记】Mysql和ES数据同步
    目录Mysql业务数据库ES查询数据库数据同步方案同步双写异步双写(MQ方式)基于Mysql的定时扫描同步基于Binlog实时同步使用canal监听binlog同步数据到es(流行方案)拓展:mysql的主从复制原理canal原理:数据迁移同步工具Mysql业务数据库核心特点:开源免费、高并发、稳定、......
  • 【java面经】微服务架构速记
    目录由来是什么本质和单体架构的区别适用项目开发框架SpringCloud(流行)DubboDropwizardConsul,etcd&etc(微服务的模块)由来独立系统,SOA服务切换时间长,成本高,不够稳定是什么一套小服务来开发单个应用,每个服务运行在自己的进程中,使用HTTPAPI等轻量级机制通信服务可......
  • 测试类 测试 java
    1importcom.alibaba.fastjson.JSON;2importcom.alibaba.fastjson.JSONObject;3importcom.lgq.ai.YouProjectApplication;4importcom.lgq.ai.service.XxxService;5importorg.junit.Test;6importorg.junit.runner.RunWith;7importorg.springframewor......
  • JAVA开源项目 甘肃非物质文化网站 计算机毕业设计
    本文项目编号T042,文末自助获取源码\color{red}{T042,文末自助获取源码}......
  • JAVA开源项目 房屋租赁系统 计算机毕业设计
    本文项目编号T041,文末自助获取源码\color{red}{T041,文末自助获取源码}......