Java内存模型与final域:重排序规则解析

0 下载量 150 浏览量 更新于2024-09-01 收藏 169KB PDF 举报
"深入理解Java内存模型,重点讨论了final关键字在内存模型中的特殊规则,包括对final域的写入与引用赋值的不可重排序以及读取final域的限制。" Java内存模型(JMM)是Java多线程编程中的一个重要概念,它规定了如何处理线程间的共享数据和通信。在Java中,final关键字用于声明不可变的变量,对于final域,JMM提供了额外的保证,以确保其特殊的行为。 首先,我们要明确两个关键的重排序规则: 1. 构造函数内的final域写入与将构造对象的引用赋值给引用变量之间不允许重排序。这意味着一旦对象构造完成,final域的值就被确定下来,其他线程无法看到这个对象的不完整状态,即final域未初始化的值。 2. 初次读取包含final域的对象引用与初次读取该final域本身之间不允许重排序。这保证了读线程在能够访问对象引用后,就能看到final域的正确初始化值。 以下是一个示例代码`FinalExample`,用于解释这两个规则: ```java public class FinalExample { int i; // 普通变量 final int j; // final变量 static FinalExample obj; public 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()`方法创建并初始化`FinalExample`对象,而线程B执行`reader()`方法读取对象的成员。由于final域的重排序规则,线程B在获取到`obj`引用后,读取`j`的值时,可以确保`j`已经被正确初始化为2,即使`i`可能还没有被初始化。 为了实现这些规则,JVM会在final域写入后插入一个StoreStore屏障,防止处理器将final域的写操作重排序到构造函数之外。同时,JMM也阻止编译器进行这种重排序。这样,当线程B读取到`FinalExample`对象的引用时,它会看到一个final域已经初始化的对象,保证了数据一致性。 总结来说,Java内存模型对final域的特殊处理确保了线程安全性和可见性,使得final变量在多线程环境中能够作为不可变对象的基石,为并发编程提供了一种可靠的机制。理解并正确运用这些规则对于编写高效且正确的并发程序至关重要。