1.Java内存模型基础
1.1 Java内存模型的抽象结构
在Java中,所有实例、静态域和数组元素都存储在堆内存中,堆内存在线程之间共享。局部变量、方法定义参数、异常处理器参数不会在线程中共享。
Java线程之间的通信由Java内存模型(Java Memory Model,简称:JMM)控制,JMM决定一个线程共享变量的写入核实对其他线程可见,即通过控制主内存和每个线程的本地内存之间的交互。抽象的结构示意图如下所示:
1.2 从源码到指令序列的重排序
1.2.1 定义
(1)编译器优化的重排序;在不改变单线程程序语义的前提下;
(2)指令级并行的重排序;在不存在数据依赖性的前提下;
(3)内存系统的重排序;
1.2.2 数据依赖性(单个处理器和单个线程)
如果两个操作访问同一个变量,且这两个操作中有一个为写操作,辞职这两个操作之间存在数据依赖性。
(1)写后读,写一个变量后,再读这个变量
(2)写后写,写一个变量后,再写这个变量
(3)读后写,读一个变量后,再写这个变量
1.2.3 as-if-serial语义
不管怎么重排序,(单线程)程序的执行结果不能改变。
1.3 happens-before
(1)程序顺序规则:一个线程中的每个操作,happens-before于该线程中的任意后续操作;
(2)监视器锁规则:对一个锁的解锁,happens-before于随后对这个锁的加锁;
(3)volatile变量规则:对于一个volatile域的写,happens-before与任意后续对这个volatile域的读;
(4)传递性:如果A happens-before B,且B happens-before C,那么A happens-before C。
(5)start规则:如果线程A执行操作ThreadB.start(),那么A线程的ThreadB.start()操作happens-before于线程B中的任意操作。
(6)join规则:如果线程A执行操作ThreadB.join()并成功返回,那么线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回。
2. volatile的内存语义
2.1 volatile特性
(1)可见性
对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入。
(2)原子性
对任意单个volatile变量的读/写具有原子性,但是类似volatile++这种复合操作不具有原子性。
2.2 volatile内存语义的实现
(1)当第二个操作是volatile写时,不管第一个操作是什么,都不能重排序。
(2)当第一个操作是volatile读时,不管第二个操作是什么,都不能重排序。
(3)当第一个操作是volatile写,第二个操作是volatile读时,不能重排序。
2.3 volatile使用场景
参考:正确使用 Volatile 变量
3 final的内存语义
3.1 final域的重排序规则
(1)在构造函数内对一个final域的写入,与随后将把这个被构造对象的引用复制给一个变量,这两个操作之间不能重排序;禁止把final域的写重排序到构造函数外。
(2)初次读一个包含final域的对象的应用,与随后初次读这个final域,这两个操作之间不能重排序;
4 创建对象的步骤
(1)分配对象的内存空间
(2)初始化对象
(3)设置变量指向刚分配的内存地址