大家好,我是树哥。

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

这几篇文章分别讲了 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 内存模型,或许应该这么理解的更多相关文章

  1. 对Java内存模型即JMM的理解

    类似物理上的计算机系统,Java虚拟机规范中也定义了一种Java内存模型,即Java Memory Model(JMM),来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能 ...

  2. Java内存模型解惑--观深入理解Java内存模型系列文章有感(二)

    1.volatile关键字修饰的域的特性 当我们声明共享变量为volatile后,对这个变量的读/写将会很特别.理解volatile特性的一个好方法是:把对volatile变量的单个读/写,看成是使用 ...

  3. 全面理解Java内存模型(JMM)及volatile关键字(转载)

    关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解Java类加载器(ClassLoad ...

  4. 全面理解Java内存模型(JMM)及volatile关键字(转)

    原文地址:全面理解Java内存模型(JMM)及volatile关键字 关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型( ...

  5. Java内存模型原理,你真的理解吗?

    [51CTO.com原创稿件]这篇文章主要介绍模型产生的问题背景,解决的问题,处理思路,相关实现规则,环环相扣,希望读者看完这篇文章后能对 Java 内存模型体系产生一个相对清晰的理解,知其然知其所以 ...

  6. 【java多线程系列】java内存模型与指令重排序

    在多线程编程中,需要处理两个最核心的问题,线程之间如何通信及线程之间如何同步,线程之间通信指的是线程之间通过何种机制交换信息,同步指的是如何控制不同线程之间操作发生的相对顺序.很多读者可能会说这还不简 ...

  7. Java内存模型原理总结(转自51CTO)

    转载地址:http://developer.51cto.com/art/201811/587220.htm [51CTO.com原创稿件]这篇文章主要介绍模型产生的问题背景,解决的问题,处理思路,相关 ...

  8. Java并发(1)- 聊聊Java内存模型

    引言 在计算机系统的发展过程中,由于CPU的运算速度和计算机存储速度之间巨大的差距.为了解决CPU的运算速度和计算机存储速度之间巨大的差距,设计人员在CPU和计算机存储之间加入了高速缓存来做为他们之间 ...

  9. 13 Java内存模型

    数据竞争 int a=0, b=0; public void method1() { int r2 = a; b = 1; } public void method2() { int r1 = b; ...

随机推荐

  1. .NET Core企业微信网页授权登录

    1.开发前准备 参数获取 corpid 每个企业都拥有唯一的corpid,获取此信息可在管理后台"我的企业"-"企业信息"下查看"企业ID" ...

  2. 创建第一个c程序

    创建,组织,生成 ,生成. 1.我们先创建一个win32项目. 文件->新建->项目->Visual C++ ->Win32   输入项目名称   选择项目保存位置 很重要的一 ...

  3. EF Core 的 Code First 模式

    0 前言 本文正文第一节,会对 Code First 进行基本的介绍,以及对相关名词进行说明,读者一开始可以不用在这里消耗过多时间,可以先操作一遍例子,再回过头理解. 第二节,以一个简单的例子,展示 ...

  4. ChCore Lab4 多核处理 实验笔记

    本文为上海交大 ipads 研究所陈海波老师等人所著的<现代操作系统:原理与实现>的课程实验(LAB)的学习笔记的第四篇:多核处理.所有章节的笔记可在此处查看:chcore | 康宇PL' ...

  5. 三月总结&四月计划

    三月总结 1. 主要任务 <C++设计模式>网课 ① 进展: 看完,对设计模式的整体设计思路和几个重要的设计模式重点学习了一下,目前对设计模式的认识还比较浅 ② 总结: 做完总结 ③ 反思 ...

  6. django的下载安装,目录结构的介绍,简单的django应用

    1.django的下载安装 pip3 install django==1.11.9 2.django的创建 1.在windows的cmd窗口下 1.1创建django项目 django-admin s ...

  7. drools中query的使用

    一.背景 我们知道在drools中是存在工作内存的,我们的Fact对象会加入到工作内存中,同时我们自己也可以在drl文件中使用insert/modify/update/delete等方法,修改工作内存 ...

  8. DevOps之敏捷开发

    初步了解一下敏捷开发及其流程 1 为什么要敏捷开发? 敏捷开发描述了一套软件开发的价值和原则,在这些开发中,需求和解决方案皆通过自组织跨功能团队达成. 1.1 背景与动机 当需求的不明确性和工程实现的 ...

  9. 干掉Switch-Case、If-Else----订阅发布模式+事件驱动模式

    在上位机和下位机或者服务端和客户端通信的时候,很多时候可能为了赶项目进度或者写代码方便直接使用Socket通信,传输string类型的关键字驱动对应的事件,这就有可能导致程序中存在大量的Switch- ...

  10. 报‘galleryElements’

    是因为组件的data(){ //没有return{ }引起的 }