数据依赖性
如果两个操作之间访问同一个变量,且这两个操作有一个为写操作,此时这两个操作就存在数据依赖性。
编译器和处理器在重排序时,会遵守数据依赖性,编译器和处理器不会改变存在数据依赖关系的两个操作的执行顺序。
这里所说的数据依赖性针对单个处理器中的指令序列和单个线程中执行的操作,不同处理器之间和不同线程之间的数据依赖性不被编译器和处理器考虑。
as-if-serial语义
as-if-serial意思就是说:不管怎么重排序(编译器和处理器为了提高并行度),单线程程序的执行结果不能改变。
同步程序的顺序一致性效果
JMM在具体实现的基本方针是:在不改变正确同步的程序执行结果的前提下,尽可能地为编译器和处理器的优化打开方便之门。
JMM不保证对64位的long型和double型变量的写操作具有原子性。因为当JVM在一些32位的处理器上运行的时候,如果对64为数据的写操作要求原子性,会有比较大的开销,所以JVM可能会把一个64位的long/double写操作拆分成两个32位的写操作的来执行,这两个32位的写操作可能会被分配到不同的总线事务中,此时对这个64位的变量的写操作不具有原子性。
未同步程序的执行特性
对于未同步或未正确同步的多线程程序,JMM只提供最小安全性: 线程执行时读取到的值, 要么是之前某个线程写入的值, 要么是默认值(0, NULL, false), JMM保证线程读操作读到的值不会无中生有.
为了实现最小安全性, JVM在堆上分配对象时,首先会对内存空间进行清零, 然后才会在上面分配对象(JVM会同步这两个操作), 因此, 在已清零的内存空间分配对象是,域的默认初始化已经完成了.
锁的内存语义
锁的释放和获取的内存语义
当线程获取锁的时候,JMM会把该线程对应的本地内存置为无效,从而是的被监视器保护的临界区代码必须从主内存中读取共享变量.
对锁释放和锁获取的内存含义做个总结:
- 线程A释放一个锁, 实质上是线程A向接下来将要获取这个锁的某个线程发出了线程A对共享变量所做出了修改的消息
- 线程B获取一个锁,实质上是线程B接收了之前某个线程(即A线程)对这个变量所作出的修改的消息
- 线程A释放锁, 然后线程B获取锁, 这个过程实质上是线程A通过主内存向线程B发送消息.