上一篇文章:锁的内存语义:https://blog.csdn.net/pcwl1206/article/details/84921447
目 录:
与前文中介绍的锁和volatile相比,对final域的读和写更像是普通的变量访问。本文主要用来介绍final域的内存语义。
1、final域的重排序规则
对于final域,编译器和处理器要遵守两个重排序规则:
1、在构造函数内对一个 final 域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序;
2、初次读一个包含 final 域的对象的引用,与随后初次读这个 final 域,这两个操作之间不能重排序。
下面,我们通过一些示例性的代码来分别说明这两个规则:
public class FinalExample {
int i; // 普通变量
final int j; // final变量
static FinalExample obj;
public void FinalExample () { // 构造函数
i = 1; // 写普通域
j = 2; // 写final域
}
public static void writer () { // 写线程A执行
obj = new FinalExample ();
}
public static void reader () { // 读线程B执行
FinalExample object = obj; // 读对象引用
int a = object.i; // 读普通域
int b = object.j; // 读final域
}
}
这里假设一个线程A执行writer ()方法,随后另一个线程B执行reader ()方法。下面我们通过这两个线程的交互来说明这两个规则。
2、写final域的重排序规则
写final域的重排序规则禁止把final域的写重排序到构造函数之外。这个规则的实现包含下面2个方面:
- JMM禁止编译器把final域的写重排序到构造函数之外。
- 编译器会在final域的写之后,构造函数return之前,插入一个StoreStore屏障。这个屏障禁止处理器把final域的写重排序到构造函数之外。
现在让我们分析writer ()方法。writer ()方法只包含一行代码:finalExample = new FinalExample ()。这行代码包含两个步骤:
- 构造一个FinalExample类型的对象;
- 把这个对象的引用赋值给引用变量obj。