版權聲明:本文為部落客原創文章,未經部落客允許不得轉載。 https://blog.csdn.net/qq_17766199/article/details/50225395
第二章、單例模式
單例模式應該是日常使用最為廣泛的一種模式了。他的作用是確定某個類隻有一個執行個體,避免産生多個對象消耗過多的資源。比如對資料庫的操作時,就可以使用單例模式。
1.各種單例
(1)餓漢模式
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){
}
public static Singleton getInstance() {
return instance;
}
}
這種寫法是在類裝載時就執行個體化instance,他避免了多線程的同步問題。但是不能保證有别的方式去裝載,沒有達到懶加載。
(2)懶漢模式(線程不安全)
public class Singleton {
private static Singleton instance;
private Singleton (){
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
達到了懶加載,但是在多線程不能正常工作。
(3)懶漢模式(線程安全)
public class Singleton {
private static Singleton instance;
private Singleton (){
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
這種寫法能夠在多線程中很好的工作,但是每次調用getInstance方法都會進行同步,反應稍慢,還會造成不必要的開銷,是以者這種不建議使用。
(4)DCL單例(雙重檢查鎖定)
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){
}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
這種寫法在getSingleton方法中對singleton進行了兩次判空,第一次是為了不必要的同步,第二次是為了在null的情況下建立執行個體。我們會發現上面代碼有一個volatile關鍵字,因為在這裡會有DCL失效問題,原因是Java編譯器允許處理器亂序執行。那麼為了解決這個問題,在JDK1.5之後,具體化了volatile關鍵字,隻要定義時加上他,可以保證執行的順序,雖然會影響性能。這種方式第一次加載時會稍慢,在高并發環境會有缺陷,但是一般能夠滿足需求。
(5)靜态内部類單例模式
public class Singleton {
private Singleton (){
}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
}
這種是推薦使用的單例模式實作方式。當第一次加載Singleton類時并不會初始化INSTANCE,隻有在第一次調用getInstance方法時才會導緻INSTANCE被初始化。這種方式不僅能夠保證線程安全,也能保證單例對象的唯一性,同時也延長了單例的執行個體化。
(6)枚舉單例
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
這種方式是Effective Java作者Josh Bloch 提倡的方式,它不僅能避免多線程同步問題,而且還能防止反序列化重新建立新的對象。
(7)使用容器實作單例模式
public class SingletonManager {
private static Map<String, Object> objMap = new HashMap<String,Object>();
private Singleton() { }
public static void registerService(String key, Objectinstance) {
if (!objMap.containsKey(key) ) {
objMap.put(key, instance) ;
}
}
public static ObjectgetService(String key) {
return objMap.get(key) ;
}
}
将多種單例類型注入到一個統一的管理類中,在使用時根據key擷取對象對應類型的對象。這種方式使得我們可以管理多種類型的單例,并且在使用時可以通過統一的接口進行擷取操作,降低了使用者的使用成本,也對使用者隐藏了具體實作,降低了耦合度。
2.Android源碼中的單例模式
在Android系統中,我們經常會通過Context擷取系統級别的服務,如WindowsManagerService、ActivityManagerService等,更常用的是一個LayoutInflater的類,這些服務會在合适的時候以單例的形式注冊在系統中,在我們需要的時候就通過Context的getSystemService(String name)擷取。
3.總結
優點:
(1)由于單例模式在記憶體中隻有一個執行個體,減少了記憶體開支,特别是一個對象需要頻繁的建立、銷毀時,而且建立或銷毀時性能又無法優化,單例模式的優勢就非常明顯。
(2)單例模式可以避免對資源的多重占用,例如一個檔案操作,由于隻有一個執行個體存在記憶體中,避免對同一資源檔案的同時操作。
(3)單例模式可以在系統設定全局的通路點,優化和共享資源通路,例如,可以設計一個單例類,負責所有資料表的映射處理。
缺點:
(1)單例模式一般沒有接口,擴充很困難,若要擴充,隻能修改代碼來實作。
(2)單例對象如果持有Context,那麼很容易引發記憶體洩露。此時需要注意傳遞給單例對象的Context最好是Application Context。
---------------------
作者:唯鹿
來源:CSDN
原文:https://blog.csdn.net/qq_17766199/article/details/50225395
版權聲明:本文為部落客原創文章,轉載請附上博文連結!