类的初始化
一个正常的类初始化过程
如果父类还没有完成初始化,则先完成父类的初始化;
Class
对象处于被链接过,还未初始化的状态;- 获取锁
LC
->记录Class
对象的状态为初始化过程中->释放锁LC
;Class
对象的状态为初始化过程中- 将
class
文件中的ConstantValue
属性中记录的常数值依次赋值给每个final
、static
字段;- 执行类
C
的<clinit>
方法;- 如果类
C
的<clinit>
方法正常完成,则获取锁LC
->记录Class
对象的状态为已完成初始化->通知所有等待线程->释放锁LC
Class
对象的状态为已完成初始化- 如果类
C
的<clinit>
方法执行报错,则则获取锁LC
->记录Class
对象的状态为初始化报错->通知所有等待线程->释放锁LC
Class
对象的状态为初始化报错
如何在并发多线程环境里保证上述初始化过程的线程安全?
假设有两个线程A
和线程B
,线程A
和线程B
都执行到步骤2,开始竞争锁LC
。
方法一:
假设线程A
在竞争中获得了锁LC
,则记录Class
对象的状态为被线程A控制的初始化过程中,然后释放了锁。然后,不管在后面的哪一步被抢占,都执行如下操作:如果线程B
获得了锁LC
,发现Class
对象的状态为被线程A
控制的初始化过程中,则线程B
就释放锁LC
,开始等待一直到收到线程A
完成Class
对象初始化的通知,正常完成。
哪些情形会触发类的初始化?
- 虚拟机启动时触发
initial
类的初始化 - 子类的初始化触发父类的初始化
- 调用类库中的反射方法
- 首次调用
java.lang.invoke.MethodHandle
实例 - 虚拟机执行
new
、getstatic
、putstatic
、invokestatic
中任意一个指令