1. 设置 final 变量的原理
理解了 volatile 原理,再对比 final 的实现就比较简单了
public class TestFinal {
final int a = 20;
}
字节码
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: bipush 20
7: putfield #2 // Field a:I
<-- 写屏障
10: return
发现 final 变量的赋值也会通过 putfield 指令来完成,同样在这条指令之后也会加入写屏障,保证在其它线程读到它的值时不会出现为 0 的情况(因为int a = 20对应的字节码有多步,第一步分配空间,此时初始值为0,赋值是第二步,之间可能发生线程上下文切换,产生对共享变量a的指令交错,导致另一个线程读取到0)。
2. 获取 final 变量的原理
获取 final 变量时,根据字节码指令, 数字较小,复制到自己的栈中读取,较大,超出短整型,在常量池读取。是直接把final变量的值,复制到该(方法)线程的操作数栈中,即复制到其他类中,没有共享的操作。
不加final,在堆中读取,效率较低。