概述
使用一個私有建構函式、一個私有靜態變數以及一個公有靜態函式來實現。 私有建構函式保證了不能通過建構函式來建立物件例項,只能通過公有靜態函式返回唯一的私有靜態變數。
實現
-
懶漢式-執行緒不安全
以下實現中,私有靜態變數uniqueInstance被延遲例項化,這樣做的好處是,如果沒有用到該類,那麼就不會例項化uniqueInstance,從而節約資源。
public class SingLeton{
private static SingLeton uniqueInstance;
private SingLeton(){
}
public static SingLeton getUniqueInstance(){
if(uniqueInstance==null){
uniqueInstance =new SingLeton();
}
return uniqueInstance;
}
}
複製程式碼
-
懶漢式-執行緒安全
只需要對getUniqueInstance()方法加鎖,那麼在一個時間點只能有一個執行緒能夠進入該方法,從而避免了uniqueInstance進行多次例項化的問題。 但是這樣有一個問題,就是當一個執行緒進入該方法後,其他執行緒檢視進入該方法都必須等待,因此效能上有一定的損耗。
public static synchroinized SingLeton getUniqueInstance(){
if(uniqueInstance ==null){
uniqueInstance =new SingLeton();
}
return uniqueInstance;
}
複製程式碼
-
餓漢式-執行緒安全
執行緒不安全問題主要由uniqueInstance被例項化了多次,如果uniqueInstance採用直接例項化的話,就不會被例項化多次,也就不會產生執行緒不安全的問題,但是直接例項化的方式也丟失了延遲例項化帶來的節約資源的優勢。
private static SingLeton uniqueInstance = new SingLeton();
複製程式碼
-
雙重校驗鎖-執行緒安全
uniqueInstance只需要被例項化一次,之後就可以直接使用了,加鎖操作只需要對例項化那部分程式碼進行,也就是說,只有當uniqueInstance沒有被例項化時,才需要進行加鎖。 雙重校驗鎖先判斷uniqueInstance是否已經被例項化了,如果沒有被例項化,那麼才對例項化語句進行加鎖。
public class SingLeton{
private volatile static SingLeton uniqueInstance;
private SinaLeton uniqueInstance;
public static SingLeton getUniqueInstance(){
if(uniqueInstance==null){
synchronized(SingLeton.class){
if(uniqueInstance == null){
uniqueInstance = new SingLeton();
}
}
}
return uniqueInstance;
}
}
複製程式碼
考慮下面的實現,也就是隻使用了一個if語句,在uniqueInstance==null的情況下,如果兩個執行緒同時執行if語句,那麼兩個執行緒就會同時進入if語句塊內,雖然在if語句塊內有加鎖操作,但是兩個執行緒都會執行uniqueInstance=new SingLeton(),這條語句,只是早晚的問題,也就是說會進行兩次例項化,從而產生了兩個例項,因此必須使用雙重校驗鎖,也就是需要使用兩個if判斷。
if(uniqueInstance == null){
synchronized(SingLeton.class){
uniqueInstance =new SingLeton();
}
}
複製程式碼
uniqueInstance 採用volatile關鍵字修飾也是很也必要的。 uniqueInstance=new SingLeton();這段程式碼其實是分為三步執行。
- 分配記憶體空間。
- 初始化物件。
- 將uniqueInstance指向分配的記憶體地址。
但是由於JVM具有指令重排的特性,有可能執行順序變為了1>3>2,這在單執行緒情況下自然是沒有問題的,但是如果是多執行緒就有可能B執行緒獲得是一個還沒有初始化的物件以致於程式出錯。 使用使用volatile修飾,目的是禁止JVM的指令重排,保證在多執行緒環境下也能正常執行。