單例分為懶漢、餓漢模式。前者的實現比較多,但用於應用中不是必須使用的物件。後者實現簡單。以下對比較成熟的實現方式做些介紹。
一、懶漢
1.雙重校驗鎖
public class singletondoublesyn
public static singletondoublesyn getinstance()}}
return instance;
}}
這裡需要注意3點:
(1)、instance變數要加volatile關鍵字,為了防止jvm重排序。
編譯器會將這段** instance = new singletondoublesyn();//2 編譯成3句jvm指令:
memory = allocate(); // 1 給instance物件分配記憶體
init(memory); // 2 初始化剛剛分配的記憶體
instance = memory; // 3 將分配的記憶體位址賦值到instance中
不過這2、3兩條語句的執行順序是不確定的,會在編譯時指令重排序,如下所示:
memory = allocate(); // 1 給instance物件分配記憶體
instance = memory; // 3 將分配的記憶體位址賦值到instance中
init(memory); // 2 初始化剛剛分配的記憶體
執行緒a執行到3的位置,此時恰好切換到執行緒b,b先判斷instance == null,返回false,那此時b執行緒拿到的是為完成初始化的instance物件。
這一情況在變數前加上volatile可以避免。
(2)、因為絕大多數對instance的訪問是讀,因此只有在instance==null是才需要執行緒同步。
(3)、將構造方法私有化,保證單例。
2.靜態內部類
public class singletonbyinnerclass
private static class singletonholder
public static singletonbyinnerclass getinstance()
}
這裡通過jvm的類載入機制(jvm對載入類時,做了同步機制),巧妙地避免了執行緒間的競爭。
宣告乙個變數不會觸發jvm載入對應類,而當訪問了類靜態方法和靜態變數時,會觸發jvm載入類,類載入完成後會執行由編譯器根據類中static變數,static方法,static塊生成的init()方法,來初始化類。
不明白靜態內部類中的變數為何要用final修飾?
jvm在對.class檔案做了合法校驗後,會在記憶體中(方法區)開闢一塊區域,用以存放類變數,開闢完成後,會對新開闢的記憶體區域「清零」。如果乙個變數被final修飾,該變數就在此時被賦值;如果沒有final修飾,他會在對類的初始化中被賦值。無論哪種都能保證賦值,並不會被提前發布(不安全發布)。為什麼要加final?
二、餓漢
public class singletonhungry
public static singletonhungry getinstance()
}
設計模式 單例模式
單例模式 singleton pattern 是乙個比較簡單的模式,其定義如下 ensure a class has only one instance,and provide a golbal point of acess to it.確保某乙個類只有乙個例項,而且自行例項化並且向整個系統提供這個...
設計模式 單例模式
class testsingleton static public function instance return self testsingleton private function clone public function setsinvar sinvar public function ...
設計模式 單例模式
單例模式的目的是保證類在系統中只被例項化一次,由該唯一的例項來為系統提供服務.單例模式主要用於保證服務的統一,比如獲取統一的編號服務,模仿oracle的序列生成等.但單例的使用需要謹慎,特別是在需要作負載均衡的地方,因為這種程式級的單例模式實際上只能保證在乙個應用中為單例.如果被多個應用載入,還是會...