1.JMM (Java Memory Model)内存模型相关概念
模型图:
可以对应下面这张图:
这张图告诉我们在线程运行的时候有一个内存专用的一小块内存,当Java程序会将变量同步到线程所在的内存,这时候会操作工作内存中的变量,而线程 中变量的值何时同步回主内存是不可预期的。但同时Java内存模型又告诉我们通过使用关键词“synchronized”或“volatile”可以让 Java保证某些约束:
“volatile” — 保证读写的都是主内存的变量
“synchronized” — 保证在块开始时都同步主内存的值到工作内存,而块结束时将变量同步回主内存.
本地内存是什么,数据存在哪里?
1、java内存模型的抽象概念
2、并不在堆中,而是在cpu中的物理cache
可参考:http://www.cnblogs.com/dolphin0520/p/3920373.html 第一部分
Intel 的MESI协议,MESI协议保证了每个缓存中使用的共享变量的副本是一致的。它核心的思想是:当CPU写数据时,如果发现操作的变量是共享变量,即在其他CPU中也存在该变量的副本,会发出信号通知其他CPU将该变量的缓存行置为无效状态,因此当其他CPU需要读取这个变量时,发现自己缓存中缓存该变量的缓存行是无效的,那么它就会从内存重新读取。
跨核访问需要通过Memory Controller
问题:为什么需要这样?
访问快!!
就像:访问mysql时,常用结果会被缓存到redis等缓存产品一样;
Cpu与内存的交互速度远慢于cpu的运算速度为了提高处理速度,先进系统内存的数据读到内存缓存(L1,L2,L3等)
缓存行(Cache line)
为了高效地存取缓存, 不是简单随意地将单条数据写入缓存的. 缓存是由缓存行组成的, 典型的一行是64字节.
查看下面这台机器: 16个cpu 每个为4核 缓存行64字节
2.并发编程几个重要概念
2.1 三个特性
在并发编程中,我们通常会遇到以下三个问题:原子性问题,可见性问题,有序性问题。我们先看具体看一下这三个概念:
1).原子性
原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
2).可见性
可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
3).有序性
有序性:即程序执行的顺序按照代码的先后顺序执行。
2.2 重排序
JMM属于语言级的内存模型,它确保在 不同的编译器和不同的处理器平台之上, 通过禁止特定类型的编译器重排序和处理器重排序,为程序员提供一致的内存可见性保证。对于编译器冲排序,JMM的编译器重排序规则会禁止特定类型的编译器重排序(不是所有的编译器重排序都要禁止)。
对于处理器重排序,JMM的处理器重排序规则会要求java编译器在生成指令序列时,插入特定类型的 内存屏障(memory barriers,intel称之为memory fence)指令,通过内存屏障指令来禁止特定类型的处理器重排序(不是所有的处理器重排序都要禁止)。
引申:
在执行程序时为了提高性能,编译器和处理器常常会对指令做重排序。重排序分三种类型:
- 编译器优化的重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。
- 指令级并行的重排序。现代处理器采用了指令级并行技术(Instruction-Level Parallelism, ILP)来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。
- 内存系统的重排序。由于处理器使用缓存和读/写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。

上述的1属于编译器重排序,2和3属于处理器重排序。这些重排序都可能会导致多线程程序出现 内存可见性问题 。

volatile语义中内存屏障的插入策略
在每个volatile写前面插入SS
在每个volatile写后面插入SL
在每个volatile读后面插入LL,插入LS


3.volatile,final,synchronized锁语义
3.1 volatile内存语义
允许线程访问共享变量。为了确保共享变量能够被准确和一致地更新,线程应该确保通过排他锁单独获得这个变量。
1、某些情况比锁更加方便
2、内存模型确保线程可以看到的变量的值是一致的
Lock前缀的指令
java -XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=print,*类名.方法名 类名
1、将当前处理器缓存行的数据写回系统内存
2、使其他CPU里缓存了该内存地址的数据无效
缓存一致性协议:每个处理器嗅探总线上传播的数据来检查自己缓存的值是否过期,当发现缓存行对应的内存地址被修改,会将当前行设置成无效状态,再次操作,会重新从系统内存中读取。
可见性:对一个volatile变量的读,总能看到(任意线程)对这个变量最后的写入。
原子性:对任意单个volatile变量的读/写具有原子性,但类似于volatile++这种复合操作不具有原子性,obj = new Object();也不是原子性
3.2 final域的内存语义
final域,编译器和处理器遵守两个重排序规则
1、在构造器内对final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。SS屏障
2、初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序。LL屏障
防止域从构造器中溢出。旧的内存模型中,可能看到final域的值为0,稍后由于初始化,发现final域的值变为1,这是比较尴尬的,所有JSR-133增强了final的语义,只要对象正确构造(被构造对象的引用在构造器中没有溢出),那么不需要使用同步,就可以保证任意线程都能看到final域在构造器中被初始化之后的值。
同时x86处理器只允许SL重排序,所以final语义的实现其实无需增加内存屏障。
3.3 锁的内存语义
待续......
1.解决以下问题:
1).JMM内存模型中本地缓存和工作内存是什么关系?
2).重排序在什么时候进行?(JIT编译优化时进行)
3).什么是内存屏障Memory Barriers,内存屏障帮我做了什么?4).内存屏障的4种插入形式?
5).原子性 可见性 重排序 顺序一致性的解释?
6).volatile,synchronized,final的语义
7).as-id-serial,happens-before的语义
相关文章:
- JAVA虚拟机-JMM内存模型(六)
-
JAVA虚拟机-JVM性能调优(五) -
JAVA虚拟机-G1 Heap Structure(四) -
JAVA虚拟机-CMS Heap Structure(三) -
JAVA虚拟机-GC介绍和垃圾算法理解(二) -
JAVA虚拟机-Java体系结构及hotspot介绍(一)
参考:
1.聊聊我对Java内存模型的理解
3.并发编程网: JAVA内存模型