设计模式(2)--Singleton(单例模式)--创建型
生活随笔
收集整理的這篇文章主要介紹了
设计模式(2)--Singleton(单例模式)--创建型
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
1.模式定義:
單例模式確保一個類只有一個實例,而且自行實例化并向整個系統提供這個實例。2.模式特點:
(1)單例類只能有一個實例。 (2)單例類必須自己創建自己的唯一實例。 (3)單例類必須給所有其他對象提供這一實例。3.使用場景:
定義一個全局變量可以確保對象隨時都可以被訪問,但不能防止我們實例化多個對象。一個更好的解決辦法是讓類自身負責保存它的唯一實例。這個類可以保證沒有其他實例被創建,并且它可以提供一個訪問該實例的方法。這就是單例模式的模式動機。 (1)需要頻繁實例化然后銷毀的對象。 (2)創建對象時耗時過多或者耗資源過多,但又經常用到的對象。 (3)有狀態的工具類對象。 (4)頻繁訪問數據庫或文件的對象。 (5)以及其他我沒用過的所有要求只有一個對象的場景。4.模式實現(八種方式):
(1)餓漢式(靜態常量)[可用]
public class Singleton {private final static Singleton INSTANCE = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return INSTANCE;
}
} 優點:這種寫法比較簡單,就是在類裝載的時候就完成實例化。避免了線程同步問題。 缺點:在類裝載的時候就完成實例化,沒有達到Lazy Loading的效果。如果從始至終從未使用過這個實例,則會造成內存的浪費。
(2)餓漢式(靜態代碼塊)[可用]
public class Singleton {private static Singleton instance;
static {
instance = new Singleton();
}
private Singleton() {}
public Singleton getInstance() {
return instance;
}
} 這種方式和上面的方式其實類似,只不過將類實例化的過程放在了靜態代碼塊中,也是在類裝載的時候,就執行靜態代碼塊中的代碼,初始化類的實例。優缺點和上面是一樣的。
(3)懶漢式(線程不安全)[不可用]
public class Singleton {private static Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
} 這種寫法起到了Lazy Loading的效果,但是只能在單線程下使用。如果在多線程下,一個線程進入了if (singleton == null)判斷語句塊,還未來得及往下執行,另一個線程也通過了這個判斷語句,這時便會產生多個實例。所以在多線程環境下不可使用這種方式。
(4)懶漢式(線程安全,同步方法)[不推薦用]
public class Singleton {private static Singleton singleton;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
} 優點:解決上面第三種實現方式的線程不安全問題,做個線程同步就可以了,于是就對getInstance()方法進行了線程同步。 缺點:效率太低了,每個線程在想獲得類的實例時候,執行getInstance()方法都要進行同步。而其實這個方法只執行一次實例化代碼就夠了,后面的想獲得該類實例,直接return就行了。方法進行同步效率太低要改進。
(5)懶漢式(線程安全,同步代碼塊)[不可用]
public class Singleton {private static Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
singleton = new Singleton();
}
}
return singleton;
}
} 由于第四種實現方式同步效率太低,所以摒棄同步方法,改為同步產生實例化的的代碼塊。但是這種同步并不能起到線程同步的作用。跟第3種實現方式遇到的情形一致,假如一個線程進入了if (singleton == null)判斷語句塊,還未來得及往下執行,另一個線程也通過了這個判斷語句,這時便會產生多個實例。
(6)雙重檢查[推薦用]
public class Singleton {private static volatile Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
} Double-Check概念對于多線程開發者來說不會陌生,如代碼中所示,我們進行了兩次if (singleton == null)檢查,這樣就可以保證線程安全了。這樣,實例化代碼只用執行一次,后面再次訪問時,判斷if (singleton == null),直接return實例化對象。 優點:線程安全;延遲加載;效率較高。
(7).靜態內部類[推薦用]
public class Singleton {private Singleton() {}
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
} 這種方式跟餓漢式方式采用的機制類似,但又有不同。兩者都是采用了類裝載的機制來保證初始化實例時只有一個線程。不同的地方在餓漢式方式是只要Singleton類被裝載就會實例化,沒有Lazy-Loading的作用,而靜態內部類方式在Singleton類被裝載時并不會立即實例化,而是在需要實例化時,調用getInstance方法,才會裝載SingletonInstance類,從而完成Singleton的實例化。 類的靜態屬性只會在第一次加載類的時候初始化,所以在這里,JVM幫助我們保證了線程的安全性,在類進行初始化時,別的線程是無法進入的。 優點:避免了線程不安全,延遲加載,效率高。
(8).枚舉[推薦用]
public enum Singleton {INSTANCE;
public void whateverMethod() { }
} 借助JDK1.5中添加的枚舉來實現單例模式。不僅能避免多線程同步問題,而且還能防止反序列化重新創建新的對象。可能是因為枚舉在JDK1.5中才添加,所以在實際項目開發中,很少見人這么寫過。
5.優缺點:
(1)單例模式的優點:
[1]在內存中只有一個對象,節省內存空間。 [2]避免頻繁的創建銷毀對象,可以提高性能。 [3]避免對共享資源的多重占用。 [4]可以全局訪問。 [5]靈活性:因為類控制了實例化過程,所以類可以靈活更改實例化過程。 [6]實例控制:會阻止其他對象實例化其自己的單例對象的副本,從而確保所有對象都訪問唯一實例。(2)單例模式的缺點:
[1]開銷:雖然數量很少,但如果每次對象請求引用時都要檢查是否存在類的實例,將仍然需要一些開銷。可以通過使用靜態初始化解決此問題。 [2]可能的開發混淆:使用單例對象(尤其在類庫中定義的對象)時,開發人員必須記住自己不能使用new關鍵字實例化對象。因為可能無法訪問庫源代碼,因此應用程序開發人員可能會意外發現自己無法直接實例化此類。 [3]對象生存期:不能解決刪除單個對象的問題。6.注意事項
(1)只能使用單例類提供的方法得到單例對象,不要使用反射,否則將會實例化一個新對象。 (2)不要做斷開單例類對象與類中靜態引用的危險操作。 (3)多線程使用單例使用共享資源時,注意線程安全問題。7.相關問題
(1)單例模式的對象長時間不用會被jvm垃圾收集器收集嗎
除非人為地斷開單例中靜態引用到單例對象的聯接,否則jvm垃圾收集器是不會回收單例對象的。(2)在一個jvm中會出現多個單例嗎
在分布式系統、多個類加載器、以及序列化的的情況下,會產生多個單例,這一點是無庸置疑的。那么在同一個jvm中,會不會產生單例呢?使用單例提供的getInstance()方法只能得到同一個單例,除非是使用反射方式,將會得到新的單例。代碼如下 Class c = Class.forName(Singleton.class.getName()); Constructor ct = c.getDeclaredConstructor(); ct.setAccessible(true); Singleton singleton = (Singleton)ct.newInstance(); 這樣,每次運行都會產生新的單例對象。所以運用單例模式時,一定注意不要使用反射產生新的單例對象。?(3)懶漢式單例線程安全嗎
主要是網上的一些說法,懶漢式的單例模式是線程不安全的,即使是在實例化對象的方法上加synchronized關鍵字,也依然是危險的,但是筆者經過編碼測試,發現加synchronized關鍵字修飾后,雖然對性能有部分影響,但是卻是線程安全的,并不會產生實例化多個對象的情況。? (4)單例模式只有餓漢式和懶漢式兩種嗎
餓漢式單例和懶漢式單例只是兩種比較主流和常用的單例模式方法,從理論上講,任何可以實現一個類只有一個實例的設計模式,都可以稱為單例模式。(5)單例類可以被繼承嗎
餓漢式單例和懶漢式單例由于構造方法是private的,所以他們都是不可繼承的,但是其他很多單例模式是可以繼承的,例如登記式單例。(6)餓漢式單例好還是懶漢式單例好
在java中,餓漢式單例要優于懶漢式單例。C++中則一般使用懶漢式單例。轉載于:https://www.cnblogs.com/yysbolg/p/7380568.html
總結
以上是生活随笔為你收集整理的设计模式(2)--Singleton(单例模式)--创建型的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Eclipse修改web项目的jdk编译
- 下一篇: asp.net mvc 接入阿里大于