JMM

什么是JMM

JMM是Java内存模型,不同的硬件生产商家和不同的操作系统下,内存的访问有一定的差异,java内存模型屏蔽掉各种硬件和操作系统的内存访问差异,以实现让java程序在各种平台下都能达到一致的并发效果。

Java内存模型规定所有的变量都存储在主内存中,包括实例变量,静态变量,但是不包括局部变量和方法参数。每个线程都有自己的工作内存,线程的工作内存保存了该线程用到的变量和主内存的副本拷贝,线程对变量的操作都在工作内存中进行线程不能直接读写主内存中的变量

不同的线程之间也无法访问对方工作内存中的变量。线程之间变量值的传递均需要通过主内存来完成。

每个线程的工作内存都是独立的,线程操作数据只能在工作内存中进行,然后刷回到主存。这是 Java 内存模型定义的线程基本工作方式。

Java内存模型所带来的问题

  • 可见性问题

    CPU中运行的线程从主存中拷贝共享数据到它的本地内存,并在之后对这个变量在本地内存中做出了更改,但这个变更对运行在其他CPU中的线程是不可见地,因为这个更改还没有flush到主存中:要解决共享对象可见性这个问题,我们可以使用volatile关键字或者是加锁。

  • 竞争现象

    线程A和线程B共享一个对象obj。假设线程A从主存读取Obj.count变量到自己的本地内存中,同时,线程B也读取了Obj.count变量到它的CPU缓存,并且这两个线程都对Obj.count做了加1操作。此时,Obj.count加1操作被执行了两次,不过都在不同的本地内存中。如果这两个加1操作是串行执行的,那么Obj.count变量便会在原始值上加2,最终主存中的Obj.count的值会是3。然而如果线程A读取count到自己的本地内存,并且在未更改count的情况下被剥夺了时间片,这时线程B读取count到本地内存,然后两个线程执行加1操作,不管是线程A还是线程B先flush计算结果到主存,最终主存中的Obj.count只会增加1次变成2,尽管一共有两次加1操作。 要解决上面的问题我们可以使用java synchronized代码块。

Java内存模型的原子性,可见性,有序性

1. 原子性

原子性指的是一个操作是不可分割,不可中断的,一个线程在执行时不会被其他线程干扰。

面试官拿笔写了段代码,下面这几句代码能保证原子性吗

int i = 2;
int j = i;
i++;
i = i + 1;

第一句是基本类型赋值操作,必定是原子性操作。

第二句先读取i的值,再赋值到j,两步操作,不能保证原子性。

第三和第四句其实是等效的,先读取i的值,再+1,最后赋值到i,三步操作了,不能保证原子性。

JMM只能保证基本的原子性,如果要保证一个代码块的原子性,提供了monitorenter 和 moniterexit 两个字节码指令,也就是 synchronized 关键字。因此在 synchronized 块之间的操作都是原子性的。

2. 可见性

可见性指当一个线程修改共享变量的值,其他线程能够立即知道被修改了。Java是利用volatile关键字来提供可见性的。 当变量被volatile修饰时,这个变量被修改后会立刻刷新到主内存,当其它线程需要读取该变量时,会去主内存中读取新值。而普通变量则不能保证这一点。

除了volatile关键字之外,final和synchronized也能实现可见性。

synchronized的原理是,在执行完,进入unlock之前,必须将共享变量同步到主内存中。

final修饰的字,一旦初始化完成,如果没有对象逸出(指对象为初始化完成就可以被别的线程使用),那么对于其他线程都是可见的。

3. 有序性

在Java中,可以使用synchronized或者volatile保证多线程之间操作的有序性。实现原理有些区别:

volatile关键字是使用内存屏障达到禁止指令重排序,以保证有序性。

synchronized的原理是,一个线程lock之后,必须unlock后,其他线程才可以重新lock,使得被synchronized包住的代码块在多线程之间是串行执行的。

八种内存交互操作

lock(锁定),作用于主内存中的变量,把变量标识为线程独占的状态。

read(读取),作用于主内存的变量,把变量的值从主内存传输到线程的工作内存中,以便下一步的load操作使用。

load(加载),作用于工作内存的变量,把read操作主存的变量放入到工作内存的变量副本中。

use(使用),作用于工作内存的变量,把工作内存中的变量传输到执行引擎,每当虚拟机遇到一个需要使用到变量的值的字节码指令时将会执行这个操作。

assign(赋值),作用于工作内存的变量,它把一个从执行引擎中接受到的值赋值给工作内存的变量副本中,每当虚拟机遇到一个给变量赋值的字节码指令时将会执行这个操作。

store(存储),作用于工作内存的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用。

write(写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中。

unlock(解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。

JMM对8种内存交互操作制定的规则

  • 不允许read、load、store、write操作之一单独出现,也就是read操作后必须load,store操作后必须write。
  • 不允许线程丢弃他最近的assign操作,即工作内存中的变量数据改变了之后,必须告知主存。
  • 不允许线程将没有assign的数据从工作内存同步到主内存。
  • 一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是对变量实施use、store操作之前,必须经过load和assign操作。
  • 一个变量同一时间只能有一个线程对其进行lock操作。多次lock之后,必须执行相同次数unlock才可以解锁。
  • 如果对一个变量进行lock操作,会清空所有工作内存中此变量的值。在执行引擎使用这个变量前,必须重新load或assign操作初始化变量的值。
  • 如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量。
  • 一个线程对一个变量进行unlock操作之前,必须先把此变量同步回主内存。

参考:https://juejin.cn/post/6874583227193884685#heading-12

【多线程与高并发】JMM内存模型 基础相关推荐

  1. Java并发-JMM内存模型

    1.线程与JVM 2.JVM内存模型与Java内存模型的区别 3.硬件内存架构与Java内存模型 4.Java内存模型对并发特征的保证 一.基本概念 程序:代码,完成某一件任务,代码序列(静态的概念) ...

  2. java多线程实例_多线程&高并发(全网最新:面试题+导图+笔记)面试手稳心不慌...

    前言 当你开始开始去跳槽面试的时候,明明只是一份15K的工作,却问你会不会多线程,懂不懂高并发,火箭造得让你猝及不防,结果就是凉凉:现如今市场,多线程.高并发编程.分布式.负载均衡.集群等可以说是现在 ...

  3. JMM内存模型如何为并发保驾护航

    一.为何引入JMM 每个处理器在执行任务时,不可能单靠"计算"就可以完成所有任务,处理器至少需要和内存交互,进行读取运算数据.存储运算结果等,这个I/O操作是很难消除掉的.但由于计 ...

  4. java并发编程系列-内存模型基础

    java线程之间的通信对程序开发人员是完全透明的,内存的可见性问题很容易困扰很多开发人员.本篇博文将揭开java内存模型的神秘面纱,来看看内存模型到底是怎样的. 并发编程中需要处理的两个关键问题: · ...

  5. java 并发模型总类_java并发编程系列-内存模型基础

    java线程之间的通信对程序开发人员是完全透明的,内存的可见性问题很容易困扰很多开发人员.本篇博文将揭开java内存模型的神秘面纱,来看看内存模型到底是怎样的. 并发编程模型的分类 并发编程中需要处理 ...

  6. 多线程与高并发基础一(超发--多线程悲观锁,乐观锁、类数据库悲观锁乐观锁)

    PS:看完文章后对自己以前所做过的并发和锁机制有了深入原理的了解. 知其然和知其所以然! 遂以记之! 关键词: 线程,同步,单例,高并发,高访问,死锁 一.大规模并发带来的挑战 在过去的工作中,我曾经 ...

  7. JUC多线程:JMM内存模型与volatile内存语义

    一.JMM 内存模型: 1.什么是 JMM 内存模型: Java 内存模型是 Java 虚拟机定义的一种多线程访问 Java 内存各个变量的访问规范,主要围绕如何解决并发过程中的原子性.可见性.有序性 ...

  8. java基础巩固-宇宙第一AiYWM:为了维持生计,多高(多线程与高并发)_Part1~整起(线程与进程篇:线程概念、线程状态、线程死锁)

    这个题目我感觉很多大哥大姐和我一样,虽然夹在众位大哥大姐中跟着一块喊着"多线程与高并发"的口号,但是这里面其实包含的东西并不像名字里面这么少.现在就开始咱们的旅程吧. 特此感谢,低 ...

  9. 深入理解并发内存模型||JMM与内存屏障||多核并发缓存架构 ||JMM内存模型||volatile 关键字的作用 ||JMM 数据原子操作||JMM缓存不一致的问题

    深入理解并发内存模型||JMM与内存屏障 多核并发缓存架构 JMM内存模型 volatile 关键字的作用 JMM 数据原子操作 JMM缓存不一致的问题

最新文章

  1. taobao sass
  2. 使用SSH上传部署WAR包到服务器
  3. 互联网中网站建设如何体现出企业品牌的企业网络推广能力?
  4. oracle得到日期对应的星期
  5. Maven 创建web项目
  6. html5 自动生成迷宫,HTML5 Canvas随机迷宫生成动画
  7. 安卓androidstudio访问本地接口_安卓开发之数据存储在本地的四种方式
  8. leetcode - 206. 反转链表
  9. 穷人为什么会越穷,穷的时候以下三个事情不能干
  10. ipqc的工作流程图_IPQC流程图
  11. SSL的4阶段握手过程
  12. android java反编译
  13. 灵悟礼品网上专卖店——画出E-R图
  14. 理解PHP网页运行原理
  15. 海尔电商峰值系统架构设计最佳实践
  16. Linux性能测试工具之Disk(四)
  17. 求星期算法c语言程序,C语言根据指定日期计算是星期几
  18. leveldb代码阅读笔记(一)
  19. 清明节网站变灰是如何实现的
  20. 数据结构之树从入门到如土(一)----大话二叉树 及GO实现

热门文章

  1. 漫步者的蓝牙耳机和南卡耳机哪个好?半入耳式耳机对比!
  2. 没有全景相机,普通人如何用krpano做属于自己的全景图
  3. 【Python学习】transpose函数
  4. Mysql ESCAPE 用法
  5. 会声会影浪漫婚礼视频——美到想哭
  6. 【计算机基础】 --- LSB、MSB与大/小端字节序
  7. 清默网络——RIP单播更新
  8. org.wltea.analyzer.lucene.IKAnalyzer(IK分词器)
  9. libxml2下载,下载地址大全
  10. 图深度学习——卷积神经网络循环神经网络自编码器