起点:
在旧的java内存模型中,线程可能会看到final域的值会改变,比如,一个线程当前看到一个整型final域的值为0(还未初始化之前的默认值),过一段时间之后这个线程再去读取这个final域的值的时候,会发现值变为了1(被其他线程初始化之后的值)。常见的就是String类型的值可能会改变。
JSR-133 专家组增强了final域的语义,即通过为final域添加写和读的重排序规则,从而达到为java程序员提供初始化安全保证:只要对象是正确构造的(没有发生被构造对象的引用在构造函数中溢出),那么不需要使用同步,就可以保证任意线程都能看到这个final域在构造函数中被初始化后的值。
1:写final域规定
写final域的重排序规则:禁止把final域的写重排序到构造函数之外,实现机制是编译器会在final域的写之后,构造函数 return 之前,插入一个StoreStore屏障,这个屏障的作用就是禁止处理器把final域的写重排序到构造函数之外。
写final域的重排序规则可以确保:在对象引用为任意线程可用之前,对象的final域已经被正确初始化了,而普通域不具备这个保障,即普通域在实际构造函数中初始化时可能会被处理器重排序到构造函数之外,
上图所示发生了普通域的写操作被编译器重排序到了构造函数之外,导致线程B在读取时还未完成初始化操作。
2:读final域的规定
在一个线程中,初次读对象引用与初次读该对象包含的final域,JMM禁止处理器重排序这两个操作,这个规则仅仅针对处理器。编译器的实现是在读final域的前面插入一个 loadload 屏障。
读final域的重排序规则可以保证:在读一个对象的final域之前,一定会先读包含这个final域的对象的引用。即如果该引用不为null,那么引用对象的final域一定已经被初始化了。