对于final域,编译器和处理器要遵守两个重排序规则:
1: 在构造函数内对一个final域的写入,与随后把这个构造对像的引用赋值给变量,不能重排序。(禁止把final域的写重排序到构造函数外,会在final域的写之后,构造函数return之前插入一个storestore屏障)
2:
finalExample obj= new FinalExample ()。这行代码包含两个步骤:
1: 构造一个FinalExample类型的对象。
2: 把这个对象的引用赋值给引用变量obj
假设线程b读取对象引用和读对象成员域之间没有重排序,

上图中,写普通域的操作被编译器重排序到构造函数之外,读线程b错误的读取了普通变量i初始化之前的值,但final域限定在构造函数之内。final域的重排序规则可以确保在对象引用为任意线程可见之前,final域已经被初始化过了。
在一个线程中,初次读对象引用和初次读该对象包含的final域,jmm禁止处理器重排序这两个操作,编译器会在读final域操作的前面插入一个loadload屏障。初次读对象引用和初次读该对象包含的final域,这两个操作之间存在间接依赖关系。由于编译器遵守间接依赖关系,编译器不会重排序这两个操作,大多数处理器也会遵守间接依赖,大多数处理器也不会重排序这两个操作。但有的处理器会,这个规则就是专门这种处理器的。

上图中,读对象的普通域操作被处理器重排序到读对象引用之前,该域还没有被线程a写入,这是一个错误的读取操作。而读final域会将其限定在读对象的引用之后。
网友评论