Java的單例模式常見的分為懶漢式、餓漢式、靜態內部類、列舉
Java的單例模式常見的分為懶漢式、餓漢式、靜態內部類、列舉
通過單例模式可以保證系統中一個類只有一個例項而且該例項易於外界訪問,從而方便對例項個數額控制並節約系統資源。
首先要了解類載入過程中的最後一個階段:即類的初始化,類的初始化階本質就是執行類構造器的
那麼回到這個程式碼中,這裡的靜態變數的賦值操作進行編譯之後實際上就是一個
static作用:
1、修飾函式的區域性變數:
特點:有預設值0,只執行一次,執行一開始就開闢了記憶體,記憶體放在全域性
2、修飾全域性函式和全域性變數:
特點:只能在本原始檔使用
3、修飾類裡面的成員變數:
特點:和1差不多,定義多個static y,但只有一個y,不進入類的大小計算,不依賴於類物件的存在而存在(可直接呼叫,要進行外接宣告)
4、修飾類的成員函式:
特點:f():括號裡無this指標,只能呼叫他的本類靜態函式和他的靜態變數,即是用static修飾過的不依賴於類物件的存在而存在(可不進行外接宣告,直接呼叫)
餓漢式:
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
特點:
在這個類初始化時就建立了物件, 每次呼叫都返回同一個物件。
餓漢模式是執行緒安全的,可以直接用於多執行緒而不會出現問題。但是不需要這個物件時,物件就會佔用記憶體空間。
為什麼執行緒安全:根據jvm載入來看,同一個載入器下,一個型別只會初始化一次,而類初始化時就建立了物件,所以 每次呼叫都返回同一個物件。
懶漢式:
public class Singleton {
private static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if(instance == null){
instance =new Singleton();
}
return instance;
}
特點:
在呼叫獲取物件的方法時判斷, 如果沒有這個物件才會建立。
這樣在多執行緒的環境下會存線上程安全問題,JVM出於對效率的考慮,是在happens-before原則內(out-of-order)亂序執行。
為什麼執行緒不安全:當多個執行緒一起去呼叫時可能某個執行緒載入了類 但是當進入getInstance() 時候 存在大家拿到的instance == null 的問題 所以會一起執行new Singleton(); 存在多Singleton個例項;
懶漢式執行緒安全問題解決方法:DCL模式(double-check-locking):
class Singleton {
public static Bank instance = null;
public Singleton(){ }
//效率更高的、執行緒安全的懶漢模式
public volatile static Singleton getInstance(){
//外面加一層判斷,若物件不為Null,就讓後面的執行緒直接返回物件,不用再排隊擠進去
if(instance == null){
synchronized (Singleton.class){//使用synchronized關鍵字,保證只有一次只有一個執行緒進入,建立例項物件
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
靜態內部類:
public class SingleTon{
private SingleTon(){}
private static class SingleTonHoler{
private static SingleTon INSTANCE = new SingleTon();
}
public static SingleTon getInstance(){
return SingleTonHoler.INSTANCE;
}
}
特定:
這是官方推薦的。外部類載入時並不需要立即載入內部類,內部類不被載入則不去初始化INSTANCE,故而不佔記憶體。不僅能確保執行緒安全,也能保證單例的唯一性,同時也延遲了單例的例項化。
為什麼執行緒不安全:原理上優化了懶漢式會造成資源浪費的情況 也是初始化時候就賦值 實現了 執行緒安全;
列舉:
public enum SingleTon{
INSTANCE;
public void method(){
//TODO
}
}
特點:
列舉在java中與普通類一樣,都能擁有欄位與方法,而且列舉例項建立是執行緒安全的,在任何情況下,它都是一個單例。我們可直接以下面這種方式呼叫。
為什麼執行緒安全:列舉是繼承了enum的一個類 實際上 就算DCL的懶漢也會被反射破壞掉執行緒安全 ,但是列舉自帶就不能被反射破壞