單例模式這樣寫比較嚴謹

我先貼出正確的寫法,在分析(注意標紅部分):


注意點已經在代碼標紅,下面分析巧妙之處:

(1) 避免鎖定整個 getInstance() ,如果鎖定整個獲取實例的方法,那麼多線程每次獲取的時候,都有可能等待,等其他線程執行完,會有性能的損失。所以在先在(1)處判斷一下,非空的話,直接拿出來用。

(2)第二個if(instance==null) 是因爲,進入同步塊的時候,可能其他線程已經創建完畢,所以再判斷一下。

(3)volatile 的作用是多線程之間,保證變量instance的可見性,避免因爲每個線程的工作內存裏面的變量instance 的值不同而取髒數據。這裏我簡單說一下工作內存:每個線程有自己的工作內存的,如果工作內存沒有它要用的對象,它會到主內存去讀,它更新這個對象的時候,它會先寫到工作內存,然後再寫到主內存。由於每個線程都有自己的工作內存,所以可能會讀髒數據。當對象加了volatile ,就會保證 該對象一旦改變,會寫到主內存,並且通知其他線程到主內存去讀。

instance = new Singleton();  這句在執行的時候,可以分解爲3行僞代碼如下:

1 memory=allocate();// 分配內存 相當於c的malloc

2 ctorInstanc(memory) //初始化對象

3 instance=memory //設置instance指向剛分配的地址


上面的代碼在編譯器運行時,可能會出現重排序 從1-2-3 排序爲1-3-2

如此在多線程下就會出現問題

例如現在有2個線程A,B

線程A在執行new Singleton()時,B線程進來,而此時A執行了 1和3,沒有執行2,此時B線程判斷instance不爲null (分配了空間即非空,但是對象還沒初始化), 直接返回一個未初始化的對象,就會出現問題

而用了volatile,上面的重排序就會在多線程環境中禁止,不會出現上述問題。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章