java线程 -> 线程工作内存 -> 主物理内存

线程工作内存的原理是栈内是连续的小空间,寻址速度比堆快得多,将变量拷贝到栈内生成副本再操作

什么是重排序

代码指令可能并不是严格按照代码语句顺序执行的。

大多数现代微处理器都会采用将指令乱序执行的方法,在条件允许的情况,直接运行当前有能力立即执行的后续指令,避免造成等待(CPU的操作速度远快于与物理内存通信的速度),大大提高执行效率。JIT编译器也会做指令重排序操作。

工作内存

每个线程有一个栈,每个栈有一个工作内存,将共享变量读到工作内存后,线程使用的变量值就是自己栈内存中的变量副本了,此时主内存变量发生修改之后,线程工作内存中的值由于已经加载,不会产生对应的变化。

volatile修饰的变量,虚拟机会保证从主内存加载到工作线程的值是最新的。

as if serial

所有的操作都可以为了优化而进行重排序,但是最终的执行结果就像按顺序执行一样,如果有依赖关系,虚拟机会阻止重排序。

异常处理:java异常处理机制也会为重排序做一些特殊处理,也就是插入错误补偿代码,将程序恢复到发生异常时应有的状态。

内存可见性

为了避免处理器访问主内存的时间开销,处理器大多会利用缓存提高性能,因此,处理器上的缓存和主内存的数据并不是实时同步的,各cpu间缓存的数据也不是实时同步的

happens-before

happens-before原则保证了我们的程序执行的可预测性,内存的可见性

  • 单线程程序代码次序法则
  • 监视器锁法则,对一个监视器锁的解锁 happens-before每一个后续的加锁
  • volatile变量法则
  • 线程启动法则:thead.start() happens-before 线程里面的动作
  • 线程中的动作happen before 线程终结或thread.join
  • 传递性原则

内存屏障

内存屏障 Memory Barrier 是一种CPU指令,用于控制特定条件下的重排序和内存可见性

LoadLoad屏障 -> Load1; LoadLoad; Load2 load2及后续读取操作要读取的数据被访问前,load1要读取的数据被读取完毕

StoreStore屏障 Store2及后续写入操作执行前,保证Store1的写入操作对其他处理器可见(刷新到内存)

LoadStore屏障 load1数据装载,之前于Store及后续的存储指令刷新到内存

StoreLoad屏障 保证Store的写入对所有处理器可见先于load操作。该屏障之前的所有内存访问指令(存储和load)完成之后,才执行屏障后的指令它的开销最大,兼具其他3种内存屏障的功能,因此它开销会很昂贵

内存屏障的作用是禁止重排序,虽然表面上是禁用单线程执行的指令重排序,但是间接影响到多线程之前的指令重排序,如保证变量b被操作前,先于b的写操作一定能被其他线程访问到

Thread A:
a=1;
b=true; Thread B:
if(b){
c = a + 1;
}

volatile

当一个操作是volatile写时,与前面的任何类型的读写都不能重排序,但可以与后面的普通读写重排序

当一个操作是volatile读时,与后面任何类型的操作都不能重排序,与前面的普通读写可以重排序

volatile读与写不能重排序

volatile写使用StoreStore内存屏障,保证前面所有的普通写操作已经对任意处理器可见了,因为StoreStore屏障将保障上面所有的普通写在volatile写之前刷新到主内存

当写入一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存

当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效,从主内存中读取所有的共享变量。

class VolatileExample {
int a = 0;
volatile boolean flag = false; public void writer() {
a = 1; //1
//StoreStore,a对flag可见
flag = true; //2
//StoreLoad,flag和a对后续可见
} public void reader() {
//LoadLoad,flag和a可见
if (flag) { //3
//LoadStore,flag和a可见
int i = a; //4
……
}
}

编译器如果能证明volatile变量只能被单线程访问,那么就可能会把它作为普通变量处理

使用volatile关键字仅能实现对原始变量(如boolen、 short 、int 、long等)操作的原子性

final

对final语义的扩展保证一个对象的构建方法结束前,所有final成员变量都必须完成初始化(的前提是没有this引用溢出)。

CAS在JDK5中被J.U.C包广泛使用,在JDK6中被应用到synchronized的JVM实现中,因此在JDK5中J.U.C的效率是比synchronized高不少的,而到了JDK6,两者效率相差无几,而synchronized使用更简单、更不容易出错,所以它是专家组推荐的首选,除非需要用到J.U.C的特殊功能(如阻塞一段时间后放弃,而不是继续等待)。

参考文章

http://www.cnblogs.com/mengheng/p/3491092.html

https://tech.meituan.com/MySQL_PingCAP_Practice.html

http://www.cnblogs.com/chenyangyao/p/5269622.html

https://blog.csdn.net/u011663071/article/details/78964991

java 内存可见性的更多相关文章

  1. 一个Java内存可见性问题的分析

    如果熟悉Java并发编程的话,应该知道在多线程共享变量的情况下,存在“内存可见性问题”: 在一个线程中对某个变量进行赋值,然后在另外一个线程中读取该变量的值,读取到的可能仍然是以前的值: 这里并非说的 ...

  2. Java内存可见性

    如果一个线程对共享变量的修改,能够被其它线程看到,那么就能说明共享变量在线程之间是可见的.如果一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几个线程的共享变量.Java内存模型(Java ...

  3. 从一个小例子引发的Java内存可见性的简单思考和猜想以及DCL单例模式中的volatile的核心作用

    环境 OS Win10 CPU 4核8线程 IDE IntelliJ IDEA 2019.3 JDK 1.8 -server模式 场景 最初的代码 一个线程A根据flag的值执行死循环,另一个线程B只 ...

  4. Java内存可见性volatile

    概述 JMM规范指出,每一个线程都有自己的工作内存(working memory),当变量的值发生变化时,先更新自己的工作内存,然后再拷贝到主存(main memory),这样其他线程就能读取到更新后 ...

  5. 从原子类和Unsafe来理解Java内存模型,AtomicInteger的incrementAndGet方法源码介绍,valueOffset偏移量的理解

    众所周知,i++分为三步: 1. 读取i的值 2. 计算i+1 3. 将计算出i+1赋给i 可以使用锁来保持操作的原子性和变量可见性,用volatile保持值的可见性和操作顺序性: 从一个小例子引发的 ...

  6. 1 Java线程的内存可见性

    Java内存的可见性 可见性: 一个线程对共享变量的修改,能够及时被其它线程看到 共享变量: 如果一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几个线程的共享变量 Java内存模型(JM ...

  7. 细说Java多线程之内存可见性

    编程这些实践的知识技能,每一次学习使用可能都会有新的认识 一.细说Java多线程之内存可见性(数据挣用)         1.共享变量在线程间的可见性                共享变量:如果一个 ...

  8. Java内存模型——可见性

    /** * 可见性问题 * @author Snway * */public class Visibility {        private static boolean stop;        ...

  9. Java内存模型JMM与可见性

    Java内存模型JMM与可见性 标签(空格分隔): java 1 何为JMM JMM:通俗地讲,就是描述Java中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取变量这 ...

随机推荐

  1. 喜大普奔!GitHub中文版帮助文档上线了!

    日前,GitHub 文档的简体中文正式发布,开发者可以到官方文档上随意查阅浏览中文文档啦!   对于想要玩 GitHub,但一直苦于英语水平较差的程序员来说,这真是一个天大的好消息.下面一起来感受一下 ...

  2. Oracle学习(八)RECORD(自定义结构)

    一.定义 记录数据类型的一种,将几个相关的.分离的.基本数据类型的变量组成一个类似于整体表结构的对象,即RECORD复合数据类型. 在使用记录数据类型变量时,需要在声明部分先定义记录的组成.记录的变量 ...

  3. 刷题[SUCTF 2019]CheckIn

    解题思路 打开网页发现只是简单做了一个上传界面,朴实无华 上传一个php文件,发现非法后缀. 上传一个.htaccess文件,发现,爆出很重要的信息 exif_imagetype函数通过检测文件头来检 ...

  4. OpenGL渲染时的数据流动

    OpenGL渲染时的数据流动 文件地址:https://wws.lanzous.com/i2aR3gu251e 链接失效记得回复哦!马上更新!

  5. react项目创建流程

    react 项目搭建 系统: windows 1.安装 node node 下载地址.一路 next 如果遇到 windows 没有权限安装 msi 文件.打开 cmd,运行msiexec /pack ...

  6. Python 中的是那种路径

    1.1 操作系统文件绝对路径 django 静态文件查找, 模板查找(第一种)   # 去配置好的 文件夹 中查找指定的文件 BASE_DIR = os.path.dirname(os.path.di ...

  7. Oracle 11G RAC11.2.0.4 + Redhat7.3安装手册

    安装思路: 1.安装两台redhat7 linux系统 2.网络配置(双网卡,public,vip,private,scan) 3.存储配置(内存配置,ASM共享存储:6块5G共享盘udev,根目录留 ...

  8. 放弃"指针常量"这种不严谨的中文描述!深度理解数组名、指针常量

    ques1: 数组名完全等价于指针常量吗? int array[10] = { 10,11,12,13,14,15 }; printf("sizeof(array)= %d \n" ...

  9. SFUD+FAL+EasyFlash典型场景需求分析,并记一次实操记录

    SFUD+FAL+EasyFlash典型场景需求分析:用整个flash存储数据,上千条数据,读取得时候用easyflash很慢,估计要检索整个flash太慢了. 改进方法:分区检索. 1存数据时,根据 ...

  10. 对offsetof、 container_of宏和结构体的理解

    offsetof 宏 #include<stdio.h> #define offsetoff(type, member)      ((int)&((type*)0)->me ...