Java内存模型与共享变量可见性

此文已由作者赵计刚授权网易云社区发布。

欢迎访问网易云社区,了解更多网易技术产品运营经验。


注:本文主要参考自《深入理解Java虚拟机(第二版)》和《深入理解Java内存模型》

1、Java内存模型(JMM)

Java内存模型的主要目标:定义在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节。

注意:上边的变量指的是共享变量(实例字段、静态字段、数组对象元素),不包括线程私有变量(局部变量、方法参数),因为私有变量不会存在竞争关系。

1.1、内存模型就是一张图:


说明:

  • 所有共享变量存于主内存
  • 每一条线程都有自己的工作内存(就是上图所说的本地内存)
  • 工作内存中保存了被该线程使用到的变量的主内存副本

注意:

  • 线程对变量的操作都要在工作内存中进行,不能直接操作主内存
  • 不同的线程之间无法直接访问对方的工作内存中的变量
  • 不同线程之间的变量的传递必须通过主内存

类比:(注意:主内存与工作内存只是一个概念,与堆栈内存没有关系,下边的类比只是帮助理解)

  • 主内存:对应于Java堆中的对象实例数据部分(注意:堆中还保存了对象的其他信息,eg.Mark Word、Klass Point和用于字节对其补白的填充数据)
  • 工作内存:对应于栈中的部分区域

1.2、8条内存屏障指令:

下面只列出6条与之后内容相关的,其余的查看《深入理解Java虚拟机》

  • lock:作用于主内存,把一个变量标识为一条线程独占的状态
  • unlock:作用于主内存,把一个处于锁定的变量解锁

下边四条是与volatile实现内存可见性直接相关的四条(store、write、read、load)

  • store:把工作内存中的变量的值传送到主内存中
  • write:把store操作从工作内存中得到的变量值放入到主内存的变量中
  • read:把一个变量的值从主内存中传输到线程的工作内存
  • load:把read操作从主内存中获取到的变量值放入工作内存的变量中去

注意:

  • 一个变量在同一时刻只允许一条线程对其进行lock操作
  • lock操作会将该变量在所有线程工作内存中的变量副本清空,否则就起不到锁的作用了
  • lock操作可被同一条线程多次进行,lock几次,就要unlock几次(可重入锁)
  • unlock之前必须先执行store-write
  • store-write必须成对出现(工作内存-->主内存)
  • read-load必须成对出现(主内存-->工作内存)

2、变量对所有线程的可见性

可见性:线程1对共享变量的修改能及时被线程2看到

2.1、共享变量不可见的原因

  • 共享变量更新后的值没有在工作内存和主内存之间及时更新
  • 线程交错执行
  • 指令重排序结合线程交错执行

2.2、实现共享变量及时更新的措施

线程1修改过共享变量后,将共享变量刷到主内存,然后,线程2从主内存读取该共享变量,将该共享变量载入到工作内存中

注意:在短时间内的高并发情况下,如果发生下列三种情况,则线程2就读不到线程1修改过的最新的值了,

  • 可能线程1根本来不及将修改过后的共享变量刷到主内存(这个时间非常短,但是还是有)的时候,线程2就已经读取了原有的主内存变量到其工作内存中。
  • 可能线程1虽然将修改过后的值刷到了主内存中,但是线程2的工作内存中的变量副本还没来得及从CPU刷新回来,所以线程2读取到的还是原来的工作内存中的变量副本
  • 可能线程1根本来不及将修改过后的共享变量刷到主内存的时候,同时,线程2的工作内存中的变量副本还没来得及从CPU刷新回来

注意:工作内存中的变量副本在使用之后,不会立刻消失掉,会一直存在,这样其值也一直不变,直到对其进行写操作或数据从CPU中刷新回来(类比volatile-read的作用)。

2.3、指令重排序:代码书写顺序与实际执行顺序不同(编译器或处理器为提高程序性能做的优化)

eg.

书写代码的顺序如下:

1         int a = 12;
2         int b = 13;
3         int c = a+b;

可能实际执行代码的顺序如下:

1         int b = 13;
2         int a = 12;
3         int c = a+b;

总结:本文大概介绍了一下Java内存模型以及与共享变量可见性的一些概念,为下边的volatile做准备。

免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐

更多网易技术、产品、运营经验分享请点击

相关文章:
【推荐】 ConcurrentHashMap源码解析(1)
【推荐】 云创大会来了!全行业关心的热点都在这里

原文地址:https://www.cnblogs.com/zyfd/p/10153784.html

时间: 2024-07-31 08:58:54

Java内存模型与共享变量可见性的相关文章

Java-JUC(二):volatile对Java内存模型中的可见性、原子性、有序性影响

Java内存模型 Java内存模型-可见性 Java内存模型-原子性 Java内存模型-有序性 volatile-是否具有可见性? volatile-是否具有原子性? volatile-是否具有有序性? 原文地址:https://www.cnblogs.com/yy3b2007com/p/8901518.html

并发编程-Java内存模型:解决可见性与有序性问题

背景 我们知道导致cpu缓存导致了可见性问题,编译器优化带来了有序性问题.那么如果我们禁用了cpu缓存与编译器优化,就能够解决问题,但是性能就无法提升了.所以一个合理的方案,就是按照一定规范来禁用缓存和编译器优化,即在某些情况下禁用缓存与编译器优化.Java内存模型就是这样的一个规范,用来解决可见性与有序性问题 概念 java内存模型本质上就是规范了JVM如何按照规则禁用缓存和编译器优化,既面向应用开发人员,也面向jvm的实现.这些规范包括了volatile.synchronized和final

从单例模式挖到内存模型(四)----java内存模型

java内存模型: Java内存模型即Java MemoryModel,简称JMM.JMM定义了Java 虚拟机(JVM)在计算机内存(RAM)中的工作方式. JDK1.5版本对java的内存模型进行了重构,开始使用新的JSR-133内存模型. JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory)或叫工作内存,本地内存中存储了该线程以读/写共享变量的副本. 注:本地内存是JMM的一个抽象概念

java内存模型一

Java平台自动集成了线程以及多处理器技术,这种集成程度比Java以前诞生的计算机语言要厉害很多,该语言针对多种异构平台的平台独立性而使用的多线程技术支持也是具有开拓性的一面,有时候在开发Java同步和线程安全要求很严格的程序时,往往容易混淆的一个概念就是内存模型.究竟什么是内存模型?内存模型描述了程序中各个变量(实例域.静态域和数组元素)之间的关系,以及在实际计算机系统中将变量存储到内存和从内存中取出变量这样的底层细节,对象最终是存储在内存里面的,这点没有错,但是编译器.运行库.处理器或者系统

(转)JAVA内存模型

(原本准备把内存模型单独放到某一篇文章的某个章节里面讲解,后来查阅了国外很多文档才发现其实JVM内存模型的内容还蛮多的,所以直接作为一个章节的基础知识来讲解,可能该章节概念的东西比较多.一个开发Java的开发者,一旦了解了JVM内存模型就能够更加深入地了解该语言的语言特性,可能这个章节更多的是概念,没有太多代码实例,所以希望读者谅解,有什么笔误来Email告知:[email protected],本文尽量涵盖所有Java语言可以碰到的和内存相关的内容,同样也会提到一些和内存相关的计算机语言的一些

java内存模型及分块

转自:http://www.cnblogs.com/BangQ/p/4045954.html 1.JMM简介 2.堆和栈 3.本机内存 4.防止内存泄漏 1.JMM简介 i.内存模型概述 Java平台自动集成了线程以及多处理器技术,这种集成程度比Java以前诞生的计算机语言要厉害很多,该语言针对多种异构平台的平台独立性而使用的多线程技术支持也是具有开拓性的一面,有时候在开发Java同步和线程安全要求很严格的程序时,往往容易混淆的一个概念就是内存模型.究竟什么是内存模型?内存模型描述了程序中各个变

Java内存模型之可见性问题

本博客系列是学习并发编程过程中的记录总结.由于文章比较多,写的时间也比较散,所以我整理了个目录贴(传送门),方便查阅. 并发编程系列博客传送门 前言 之前的文章中讲到,JMM是内存模型规范在Java语言中的体现.JMM保证了在多核CPU多线程编程环境下,对共享变量读写的原子性.可见性和有序性. 本文就具体来讲讲JMM是如何保证共享变量访问的可见性的. 什么是可见性问题 我们从一段简单的代码来看看到底什么是可见性问题. public class VolatileDemo { boolean sta

【Java并发基础】Java内存模型解决有序性和可见性

前言 解决并发编程中的可见性和有序性问题最直接的方法就是禁用CPU缓存和编译器的优化.但是,禁用这两者又会影响程序性能.于是我们要做的是按需禁用CPU缓存和编译器的优化. 如何按需禁用CPU缓存和编译器的优化就需要提到Java内存模型.Java内存模型是一个复杂的规范.其中最为重要的便是Happens-Before规则.下面我们先介绍如何利用Happens-Before规则解决可见性和有序性问题,然后我们再扩展简单介绍下Java内存模型以及我们前篇文章提到的重排序概念. volatile 在前一

深入理解Java内存模型(四)——volatile

volatile的特性 当我们声明共享变量为volatile后,对这个变量的读/写将会很特别.理解volatile特性的一个好方法是:把对volatile变量的单个读/写,看成是使用同一个锁对这些单个读/写操作做了同步.下面我们通过具体的示例来说明,请看下面的示例代码: class VolatileFeaturesExample { //使用volatile声明64位的long型变量 volatile long vl = 0L; public void set(long l) { vl = l;