以下例子參考[url]
網上閒逛時發現一篇博文,講的是單例同步鎖時失敗的可能,提到的錯誤自己基本都忽略了,下面以其中的例子說一下自己的理解。
單例模式是比較簡單直接的:
public class singleton
public static singleton getinstance()
return instance;
} }
這也是在初學單例模式時會提到的乙個採用延遲載入實現的例子,實際上這個例子並不能「保證」單例,在多執行緒高併發的情況下,如果瞬間同時初始訪問getinstance方法,返回的可能就是不同的例項。於是就有了下面的簡單的同步機制:
public class singleton
public synchronized static singleton getinstance()
return instance;
} }
這種方式保證了執行緒安全,但是我們只需要在生成例項的過程中保證其同步即可,不需要對訪問也進行加鎖。很明顯,簡單的在類例項範圍內對其加鎖,效能是其瓶頸。為了避免這種情況,便會有下面的想法:
public class singleton
public static singleton getinstance() }}
return instance;
}}
其實這樣便可以保證單例了,但是卻保證不了單例的準確性。也就是說不同的執行緒可以拿到同乙個例項,但是在某些情況下,會取到例項的錯誤狀態。在本例中,初始化singleton物件和將物件位址賦給instance的順序是不確定的。也即可能有以下兩種情況:
1、在初始化之前將物件引用賦給instance。這時,instance有了實際的引用,但物件還沒有初始化。其他執行緒此時如果獲取到instance,可能就是只有引用而還沒有初始化的例項。
2、在初始化之後將物件引用賦給instance。這是正確的狀態,保證了單例也保證了正確性。
這種不確定性不能「保證」單例的正確性。再看下面的改進方案:
public class singleton
public static singleton getinstance()
instance = temp;
} }
} return instance;
} }
這種方式製造了乙個記憶體屏障,即使用了乙個臨時變數來保證「初始化操作和賦引用操作」的原子性。這種方式從**方面看已經沒有問題了,但是注意到在同步語句塊之外的instance=temp,根據博文的解釋:[color=red]同步語句塊內的操作必須在語句塊結束之前完成,但是**中同步塊之外的操作有可能被編譯器放到塊內執行(只是存在這種可能性)。[/color]這一點細節確實不容易考慮到,需要了解一些jvm的知識,這方面沒什麼研究,保留意見。
看最後一種方式:
public class singleton
public static singleton getinstance()
} }
return instance;
} }
這裡要說一下volatile關鍵字,這個除了在一定程度上確保同步之外,jdk1.5擴充了volatile語義,簡單點說就是保證了物件初始化以及賦引用的有序性,在本例來說就是先初始化後賦引用。這樣兩層機制保證了單例的唯一性和準確性。
同步鎖2 互斥鎖
1 import threading 2import time 34 num 100 5def add 6global num7 s num 8 time.sleep 0.02 9 num s 1 1011 l list 12for i in range 100 13 t threading.thr...
執行緒同步鎖
同步鎖 對於兩個函式a,b,建立兩條執行緒,並且讓其睡眠,流程上看似輪流執行,其實不是,因為涉及到cpu切換去執行問題。import threading import time def a for i in range 3 print a str i time.sleep 0.01 def b fo...
NTP時間同步失敗
root server3 ntpdate time.nist.gov 1 mar 17 28 28 ntpdate 13040 adjust time server 132.163.97.1 offset 0.017465 sec伺服器拒絕同步,因為時間差比較大,我們可以考慮到手動同步時間。root...