java 内存可见性
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 内存可见性的更多相关文章
- 一个Java内存可见性问题的分析
如果熟悉Java并发编程的话,应该知道在多线程共享变量的情况下,存在“内存可见性问题”: 在一个线程中对某个变量进行赋值,然后在另外一个线程中读取该变量的值,读取到的可能仍然是以前的值: 这里并非说的 ...
- Java内存可见性
如果一个线程对共享变量的修改,能够被其它线程看到,那么就能说明共享变量在线程之间是可见的.如果一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几个线程的共享变量.Java内存模型(Java ...
- 从一个小例子引发的Java内存可见性的简单思考和猜想以及DCL单例模式中的volatile的核心作用
环境 OS Win10 CPU 4核8线程 IDE IntelliJ IDEA 2019.3 JDK 1.8 -server模式 场景 最初的代码 一个线程A根据flag的值执行死循环,另一个线程B只 ...
- Java内存可见性volatile
概述 JMM规范指出,每一个线程都有自己的工作内存(working memory),当变量的值发生变化时,先更新自己的工作内存,然后再拷贝到主存(main memory),这样其他线程就能读取到更新后 ...
- 从原子类和Unsafe来理解Java内存模型,AtomicInteger的incrementAndGet方法源码介绍,valueOffset偏移量的理解
众所周知,i++分为三步: 1. 读取i的值 2. 计算i+1 3. 将计算出i+1赋给i 可以使用锁来保持操作的原子性和变量可见性,用volatile保持值的可见性和操作顺序性: 从一个小例子引发的 ...
- 1 Java线程的内存可见性
Java内存的可见性 可见性: 一个线程对共享变量的修改,能够及时被其它线程看到 共享变量: 如果一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几个线程的共享变量 Java内存模型(JM ...
- 细说Java多线程之内存可见性
编程这些实践的知识技能,每一次学习使用可能都会有新的认识 一.细说Java多线程之内存可见性(数据挣用) 1.共享变量在线程间的可见性 共享变量:如果一个 ...
- Java内存模型——可见性
/** * 可见性问题 * @author Snway * */public class Visibility { private static boolean stop; ...
- Java内存模型JMM与可见性
Java内存模型JMM与可见性 标签(空格分隔): java 1 何为JMM JMM:通俗地讲,就是描述Java中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取变量这 ...
随机推荐
- SQL中只要用到聚合函数就一定要用到group by 吗?
1.当聚集函数和非聚集函数出现在一起时,需要将非聚集函数进行group by2.当只做聚集函数查询时候,就不需要进行分组了.
- 十分钟快速上手NutUI
本文将会从 NutUI 初学者的使用入手,对 NutUI 做了一个快速的概述,希望能帮助新人在项目中快速上手. 文章包括以下主要内容 安装引入 NutUI NutUI 组件的使用 NutUI 主题和样 ...
- java 并发线程池的理解和使用
一.为什么要用线程池 合理利用线程池能够带来三个好处. 第一:降低资源消耗.通过重复利用已创建的线程降低线程创建和销毁造成的消耗. 第二:提高响应速度.当任务到达时,任务可以不需要的等到线程创建就能立 ...
- 手写:javascript中的关键字new
简单介绍一下new new再熟悉不过了,new后面跟着构造函数,可以创建对象,这个对象的原型指向构造函数的原型对象,说起来可能有点绕,直接看代码吧 function Person(name, age) ...
- 刷题[WUSTCTF2020]CV Maker
解题思路 好家伙,打开一看像是cms,又看名字CV Maker.我以为直接要搜cve打了.搜了一会发现没什么啊.那先正常做把. 注册 注册成功后这里报错,猜测可能有注入点.先放在这里,继续登陆.发现是 ...
- Mall电商实战项目发布重大更新,全面支持SpringBoot 2.3.0
1. 前言 前面近一个月去写自己的mybatis框架了,对mybatis源码分析止步不前,此文继续前面的文章.开始分析mybatis一,二级缓存的实现. 附上自己的项目github地址:https:/ ...
- 动态代理:jdk动态代理和cglib动态代理
/** * 动态代理类:先参考代理模式随笔,了解代理模式的概念,分为jdk动态代理和cglib,jdk动态代理是通过实现接口的方式创建代理类的,cglib是通过继承类的方式实现的代理类的 * jdk动 ...
- Python练习题 005:三个数字由大到小排序输出
[Python练习题 005]输入三个整数x,y,z,请把这三个数由小到大输出. ----------------------------------------------------------- ...
- 02 C语言最简单快速上手的IDE
不要让开发环境 成为学习路上的拦路虎,先培养学习兴趣和学习路上的成就感,这个对于激励自己持续学习很重要. 等真正从小白入门了,甚至成为大牛了,能解决诸多困难问题了,安装个开发环境还会再是个什么难事吗? ...
- fopen和fopen_s的区别
转载:https://blog.csdn.net/keith_bb/article/details/50063075 fopen: 原型:FILE * fopen(const char * path, ...