首页 > 系统相关 >Java 内存模型,或许应该这么理解

Java 内存模型,或许应该这么理解

时间:2022-10-06 20:09:48浏览次数:85  
标签:Java 模型 关键字 理解 volatile 内存

大家好,我是树哥。

在前面一段时间,我连续写了几篇关于并发编程的文章:

这几篇文章分别讲了 Java 内存模型、happens-before 原则、volatile 关键字、synchronized 关键字、Java 对象的内存布局。这 5 篇文章看着好像是独立的,但实际上他们是互相关联的。

在好几年前我也看过 Java 内存模型这些内容,但网上的内容实在太多太杂,始终找不到合理的解释。刚好就在几周前,当我再次认真看这些内容的时候,突然发现能比较好地串起来了,所以就写了这几篇文章。今天,就树哥一起与你一起重温下这几个知识点的联系与理解吧。

Java 内存模型

网上关于 Java 内存模型的内容特别多,很多都讲到了多 CPU 与缓存的数据一致性问题,于是顺带牵出了 MESI 等缓存一致性协议。其实到这里都没问题,都挺有逻辑的。

但接下来为啥有 Java 内存模型?为啥又有 happens-before 原则?这些内容基本上没有一个说得清楚,这就让人很困惑了。此外,有些还扯出了内存屏障、执行时序的问题,但都没啥逻辑,听起来乱糟糟的。我就曾专门花了一个晚上认真看某篇很火的文章,但最终也没搞懂。

对于 Java 内存模型,我舍弃了一些不必要的细碎点,整理了我的一些理解,我感觉相对来说还是比较好理解的。

首先,由于多核 CPU 和高速缓存在存在,导致了缓存一致性问题。 这个问题属于硬件层面上的问题,而解决办法是各种缓存一致性协议。不同 CPU 采用的协议不同,MESI 是最经典的一个缓存一致性协议。

其次,操作系统作为对底层硬件的抽象,自然也需要解决 CPU 高速缓存与内存之间的缓存一致性问题。 各个操作系统都对 CPU 高速缓存与缓存的读写访问过程进行抽象,最终得到的一个东西就是「内存模型」。

从硬件到操作系统,这个是我自己的理解,我并没有找到一些资料提到这点。但我觉得这应该是没有错的。因为操作系统就是对底层硬件的抽象,而所有抽象的东西就需要定义一些概念。

对于操作系统来说,这些概念就是内存模型、CPU 时间片等。内存模型这个词,在操作系统的教科书上也是可以找到的,这也是一个佐证吧。

于是,我们从硬件层面理解到了操作系统层面,但这跟 Java 内存模型有啥关系呢?

最后,Java 语言作为运行在操作系统层面的高级语言,为了解决多平台运行的问题,在操作系统基础上进一步抽象,得到了 Java 语言层面上的内存模型,其也是为了解决多线程情况下的数据一致性问题。

我们是因为要实现 Java 语言的「Write Once, Run Anywhere」的理念,那么就必须解决多平台内存模型不一致的问题,这样才创造出了 Java 内存模型。

Java 内存模型规定了很多规则,如果 Java 程序能够遵守 Java 内存模型的规则,那么其写出的程序就是并发安全的,这就是 Java 内存模型最大的价值。

到这里,我们从硬件、操作系统再到语言层面,知道了 Java 内存模型诞生的原因,知道其诞生就是为了解决多平台的内存模型统一问题,进一步其实就是多线程的数据一致性问题。

happens-before 原则

前面说到,为了解决多平台的内存模型统一,以及多线程的数据一致性问题,所以有了 Java 内存模型。但是 Java 内存模型的内容太多了,基本就记不住,非常不利于编程人员理解,所以才有了 happens-before 原则。

所以说 happens-before 原则是对 Java 内存模型的简化,让我们更好地写出并发代码。

volatile 关键字

volatile 关键字,其实也与 Java 内存模型有关系,只是很多文章都没说清楚。

volatile 关键字有两个作用,就是可见性和禁止指令重排序。但为啥它有这两个作用呢?其实 volatile 这两个作用的来源,就来自于 Java 内存模型里对 volatile 变量定义的特殊规则。

这就是 volatile 关键字与 Java 内存模型的关系,比较简单。

至于内存屏障这个词,其实就是一个让我们方便理解的名词,诞生于 volatile 禁止指令重排序这个作用里,也没啥不好理解的。

synchronized 关键字

synchronized 关键字,也是并发编程常用到的内容,其实它和 Java 内存模型没关系,但和 Java 虚拟机规范有关系。

synchronized 关键字经过编译之后,会在同步块的前后分别形成 monitorenter 和 monitorexit 这两个字节码指令,这两个字节码的执行需要指明一个要锁定或解锁的对象。而 monitorenter 和 monitorexit 这两个字节码指令为啥能实现这样的功能,是因为 Java 虚拟机中做了强制定义,那么虚拟机就需要实现。

synchronized 关键字与 Java 对象的内存布局,也是有关系的。自旋锁、自适应锁、偏向锁,它们靠什么实现,就是 Java 对象中的对象头去判断,然后进行一系列的逻辑操作。

总结

至此,我们基本上可以把 Java 并发编程里常见的那些概念的关系搞清楚了。

Java内存模型 是对内存布局的抽象,解决多平台运行以及多线程一致性的问题。happens-before 原则 是 Java 内存模型定义的简化,方便我们学习。volatile 则是轻量级同步同步机制,其来源于 Java 内存模型赋予的权利。

synchronized 关键字的合法性,则来自于 Java 虚拟机规范。而 synchronized 中自旋锁、自适应锁、偏向锁等,都依靠 Java 对象的对象头 来判断。

以上就是我对 Java 并发编程里常见概念的理解,感觉还是比较清晰一些。如果有什么理解得不对的,欢迎一起探讨探讨~

Java 内存模型,或许应该这么理解_Java内存模型



标签:Java,模型,关键字,理解,volatile,内存
From: https://blog.51cto.com/u_13879334/5733971

相关文章

  • 从 CPU 讲起,深入理解 Java 内存模型!
    Java内存模型,许多人会错误地理解成JVM的内存模型。但实际上,这两者是完全不同的东西。Java内存模型定义了Java语言如何与内存进行交互,具体地说是Java语言运行时的变......
  • 从 HelloWorld 看 Java 字节码文件结构
    很多时候,我们都是从代码层面去学习如何编程,却很少去看看一个个Java代码背后到底是什么。今天就让我们从一个最简单的HelloWorld开始看一看Java的类文件结构。在开始......
  • 大白话说Java反射:入门、使用、原理
    反射之中包含了一个「反」字,所以想要解释反射就必须先从「正」开始解释。一般情况下,我们使用某个类时必定知道它是什么类,是用来做什么的。于是我们直接对这个类进行实例化,......
  • Java网络编程Socket
    Java网络多线程2022年8月31日笔记韩顺平Java自学笔记网络多线程_关注永雏塔菲喵的博客-CSDN博客网课【韩顺平讲Java】Java网络多线程专题-TCPUDPSocket编程多......
  • Java—多线程
    Java多线程基础概念进程与线程进程:操作系统分配资源的最小单位线程:CPU执行的最小单位线程分类1.用户线程用户自己创建的业务线程;2.守护线程......
  • java static 静态
    static是java语言中的关键字,意思是“静态的”,它可以用来修饰变量、方法、代码块等,修饰的变量叫做静态变量,修饰的方法叫做静态方法,修饰的代码块叫做静态代码块。static的......
  • 「前端料包」深入理解JavaScript原型和原型链
    1.前言关于JS原型和原型链我之前刚学js就有写过一篇学习笔记形式的博客,但前两天翻出来一看——什么鬼,这是我写的吗?自己都看不懂了,于是我重新整理思路,今天「前端料包」......
  • java方法重载
    1234567891011121314151617如下代码:<br>//MethodOverload.java //Usingoverloadedmethods packageHJssss;public class zhuce{ ......
  • Java NIO通信基础
    参考书籍:Netty、Redis、ZooKeeper高并发实战1.  IO读写的基本原理用户程序进行IO的读写,实际上是缓存区的复制。即read:从内核缓冲区复制到进程缓冲区;write:从进行缓冲区......
  • 「前端料包」一文吃透盒子模型BFC
    前言接触写博客有一段时间了,都是边学边学着写,但总感觉写的凌乱,想起啥写啥。这几天在刷红宝书,收获还是蛮多的,决定结合自己的学习,写一个系列,我叫它「前端料包」,旨在巩固前......