前言
在《Java并發變成的藝術》一書中筆者把類的初始化的處理過程分了5個階段,為了更深入的理解,我便記錄下來。
第一階段:通過在Class對象上同步(即獲取Class對象的初始化鎖),來控制類或接口的初始化。這個獲取鎖的線程會一直等待,直到當前線程能夠獲取到這個初始化鎖。
假設Class對象當前還沒有被初始化(初始化狀態state,此時被標記為state=noInitialization),且有兩個線程A和B試圖同時初始化這個Class對象。圖1是對應的示意圖。
圖1 類初始化——第1階段
表1是這個示意圖的說明。
表一
時間 | 線程A | 線程B |
---|---|---|
t1 | A1:嘗試獲取Class對象的初始化鎖。這里假設線程A獲取到了初始化鎖 | B1:嘗試獲取Class對象的初始化鎖,由于線程A獲取到了鎖,線程B將一直等待獲取初始化鎖 |
t2 | A2線程A看到線程還未被初始化(因為讀取到state=noInitialization),線程設置state=initializing | |
t3 | A3:線程A釋放初始化鎖 |
第2階段:線程A執行類的初始化,同時線程B在初始化鎖對應的condition上等待。
表2是這個示意圖的說明。
表2
時間 | 線程A | 線程B |
---|---|---|
t1 | A1:執行類的靜態初始化和初始化類中聲明的靜態字段 | B1獲取到初始化鎖 |
t2 | B2:讀取到state=initializing | |
t3 | B3:釋放初始化鎖 | |
t4 | B4:在初始化鎖的condition中等待 |
圖2 類初始化——第2階段
第3階段:線程A設置state=initialized,然后喚醒在condition中等待的所有線程。
圖3 類初始化——第3階段
表3是這個圖的說明。
表3
時間 | 線程A |
---|---|
t1 | A1:獲取初始化鎖 |
t2 | A2:設置state=initialized |
t3 | A3:喚醒在condition中等待的所有線程 |
t4 | A4:釋放初始化鎖 |
t5 | A5:線程A的初始化處理過程完成 |
第4階段:線程B結束類的初始化處理。
圖4 類初始化——第4階段
表4是這個圖的說明。
表4
時間 | 線程B |
---|---|
t1 | B1:獲取初始化鎖 |
t2 | B2:讀取到state=initialized |
t3 | B3:釋放初始化鎖 |
t4 | B4:線程B的類初始化處理過程完成 |
線程A在第2階段的A1執行類的初始化,并在第3階段的A4釋放初始化鎖;線程B在第4階段的B1獲取同一個初始化鎖,并在第4階段的B4將之后才開始訪問這個類。根據Java內存模型規范的鎖規則,這里將如下的happens-before關系。
這個happens-before關系將保證:線程A執行類的初始化時的寫入操作(執行類的靜態初始化和初始化類中聲明的靜態字段),線程B一定能看到。
第5階段:線程C執行類的初始化的處理。
圖5 類初始化——第5階段
表5是這個圖的說明。
表5
時間 | 線程C |
---|---|
t1 | C1:獲取初始化鎖 |
t2 | C2:讀取到state=initialized |
t3 | C3:釋放初始化鎖 |
t4 | C4:線程C的類初始化處理過程完成 |
在第3階段之后,類已經完成了初始化。因此線程C在第5階段的類初始化處理過程相對簡單一些(前面的線程A和B的類初始化處理過程都經歷了兩次鎖獲取-鎖釋放,而線程C的類初始化處理過程只需要經歷一次鎖獲取-鎖釋放)。
線程A在第2階段的A1執行類的初始化,并在第3階段的A4釋放鎖;線程C在第5階段的C1獲取同一個鎖,并在第5階段的C4之后才開始訪問這個類。根據Java內存模型規范的鎖規則,將存在如下的happens-before關系。
這個happens-before關系將保證:線程A執行類的初始化時的寫入操作,線程C一定能看到。