類的初始化
一個正常的類初始化過程
如果父類還沒有完成初始化,則先完成父類的初始化;
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
中任意一個指令