DCL單例模式為什麼要兩次判空

2022-05-22 19:33:10 字數 1803 閱讀 8462

public class test

private volatile static test instance;

private test() public static test getinstance()

if (instance == null)

synchronized (test.class)

} }return instance; }

然後來分析getinstance()每一步的作用

第乙個if語句,用來確認呼叫getinstance()時instance是否為空,如果不為空即已經建立,則直接返回,如果為空,那麼就需要建立例項,於是進入synchronized同步塊。

synchronized加類鎖,確保同時只有乙個執行緒能進入,進入以後進行第二次判斷,是因為,

對於首個拿鎖者,它的時段instance肯定為null,那麼進入new singleton()物件建立,

而在首個拿鎖者的建立物件期間,可能有其他執行緒同步呼叫getinstance(),那麼它們也會通過if進入到同步塊試圖拿鎖然後阻塞。

這樣的話,當首個拿鎖者完成了物件建立,之後的執行緒都不會通過第乙個if了,而這期間阻塞的執行緒開始喚醒,它們則需要靠第二個if語句來避免再次建立物件。

以上就是雙檢索的實現思路,synchronized與第二個if即是用來保證執行緒安全與不產生第二個例項,也是double_checked_lock由來。

那麼volatile的作用體現在哪呢?

一開始我認為是volatile的同步性,因為要在首個拿鎖者建立物件以後,立即保證instance的可見性,以讓被喚醒的阻塞執行緒能夠在第二個if語句的時候得到instance非空的結果。

但這個可見性其實是用synchronized來保障的,並不需要volatile來多此一舉了。

後來才知道應該是避免指令重排序,說明如下

這裡的指令重排序主要體現在instance = new singleton()這條語句上了。

這條語句顯然是個復合操作,可以簡單分下,(已完成類載入 ,假設在堆上分配記憶體)

1.在堆中分配物件記憶體

2.填充物件必要資訊+具體資料初始化+末位填充

3.將引用指向這個物件的堆內位址

那麼,在完成1後,物件的大小和位址已經確定,因此,2和3其實存在指令重排序的可能。

並且可以看到,3的操作明顯比2要少,那麼如果讓2與3一起執行,並且反應到具體的順序上變成了1-3-2.

先完成3,引用變數instance先指向了在堆中給物件分配的空間,然後2仍在慢慢吞吞繼續。

這時候,被synchronized擋在外面的阻塞執行緒其實是不會有什麼影響的,因為一定會等到物件建立完,首個拿鎖者才會釋放鎖。

那麼關鍵是在,此刻如果在3完成而2未完成這個臨界點,有乙個新執行緒呼叫getinstance(),那麼第乙個if,會怎麼樣?

答案是因為第乙個if沒在同步塊裡,而此時instance已經非空,指向具體記憶體位址了,所以直接返回此時未完成初始化的instance例項

那麼如果在singleton裡有個變數int number ,有個方法int getnumber()返回number,這時候呼叫

singleton.getinstance().getnumber();

會怎樣?

不知道,可能會報錯,或者會得到錯誤結果,但這就是我能想到的volatile避免的情況了。

volatile修飾變數避免指令重排序,保證1-2-3按順序來,這樣即使在首個拿鎖者未釋放鎖前,有執行緒切入,當它在第乙個if處得到instance非空時,此時instance的初始化也一定已經完成。

因此這就是volatile在dcl的作用了。

qemu中daemonize為什麼要兩次fork

函式void os daemonize void 將當前程序變成後台程序即放棄終端。一開始不理解為什麼要兩次fork,後來查網上資料得知,兩次fork是為了防止第乙個子程序開啟終端。首次fork使父程序退出,子程序繼承了父程序的程序組id,但具有乙個新的程序id,這就保證了子程序不是乙個程序組的首程...

daemon 程序為什麼要fork兩次

daemon 程序為什麼要fork兩次 daemon程序是後台守護程序,有時候也叫精靈程序 agent linux下server都是daemon程序。相信大部分開發人員都知道如何去寫乙個daemon程序。但是另一方面,大部分人不知道為什麼要這麼做,不少人是從某個地方copy乙個函式,拿來主義。但是具...

daemon 程序為什麼要fork兩次

daemon程序是後台守護程序,有時候也叫精靈程序 agent linux 下server都是daemon程序。相信大部分開發人員都知道如何去寫乙個daemon程序。但是另一方面,大部分人不知道為什麼要這麼做,不少人是從某個地方copy乙個函式,拿來主義。但是具體為什麼這麼實現,卻不是很透徹。見過一...