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. 2020年的100天——FLAGS

    2020年的100天--FLAGS Reading <爱的博弈>-- 作者: 约翰·戈特曼 (John Gottman) / 娜恩·西尔弗 (Nan Silver) <人间失格> ...

  2. tomcat开机自启

    https://blog.csdn.net/qq_31150365/article/details/89330366 参考连接 1,修改tomcat的catalina.sh文件 # vim /usr/ ...

  3. 关于人人开源renren-fast-vue 中npm install各种报错的解决方案

    首先吐槽一下,因为这个问题我整了好几天,把报错信息复制百度,试遍了各种方法,node.js我是卸载了安装,安装了卸载,甚至renren-fast-vue我也删了再下,然后再删,无限循环.然而没有什么软 ...

  4. Java基础——HashMap

    1.HashMap底层的实现 JDK 1.7 中 HashMap 是以数组+链表的形式组成的 JDK 1.8 之后数组+链表/红黑树的组成的,当链表大于 8 并且容量大于 64 时,链表结构会转换成红 ...

  5. Vue.js 学习笔记之三:与服务器的数据交互

    显而易见的,之前的02_toDoList存在着一个很致命的缺陷.那就是它的数据只存在于浏览器端,一但用户关闭或重新载入页面,他之前加入到程序中的数据就会全部丢失,一切又恢复到程序的初始状态.要想解决这 ...

  6. 引用类型之Array(二)

    操作方法 concat( ) concat() 方法用于连接两个或多个数组. 该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本. 语法 arrayObject.concat(arrayX,a ...

  7. Linux 杀毒软件ClamAV安装部署

    环境说明 系统安全需求,批量安装免费杀毒软件: 操作系统统一为CentOS 7 x64,在此选择免费开源杀毒软件ClamAV: 两种安装方式 1.yum 安装: 2.源码包编译安装: 安装参考网址: ...

  8. java安全编码指南之:输入注入injection

    目录 简介 SQL注入 java中的SQL注入 使用PreparedStatement XML中的SQL注入 XML注入的java代码 简介 注入问题是安全中一个非常常见的问题,今天我们来探讨一下ja ...

  9. 关于IPA文件重签名后如何跟踪管理APP的技术探讨和实践演示

    前言:开发iOS的朋友都知道,在功能开发完成后,我们就会用申请的苹果账号在后台做证书配置,然后提交到AppStore,但是也有部分APP我们不需要提交到AppStore,比如内部测试用的APP.定制给 ...

  10. CSS字体属性与文本属性

    CSS字体属性与文本属性 1. 字体属性 1.1 字体系列font-family p { font-family: "Microsoft Yahei";/*微软雅黑*/ } /*当 ...