淺談設計模式——單例模式


單例模式

  單例模式(Singleton)是一種常用的設計模式,它是創建型模式的一種,適用於一個類有且只有一個實例的情況,也就是說,單例模式確保了某個類只有一個實例(對象)存在。

單例模式定義的三個要素

  ① 定義私有的靜態成員。

  ② 構造函數私有化。

  ③ 提供一個公有的靜態方法以構造實例。

單例模式的實現方式

  對於單例模式,一定要考慮並發狀態下的同步問題,單例模式根據實例化對象時間的不同在實現代碼時分為兩種主流的實現方式,一種叫作餓漢式單例,另一種叫作懶漢式單例,這兩種實現方式都是多線程安全的,但前者是天生多線程安全。

  ★ 餓漢式單例的實現方式:在單例類被加載時,就實例化一個對象。

  ★ 懶漢式單例的實現方式:調用取得實例的方法時才會實例化對象。

餓漢式單例模式的Java 實現代碼如下:

// 單例模式(餓漢式)
public class Singleton { private static Singleton instance = new Singleton(); private Singleton() {}; public static Singleton getInstance() { return instance; } }

懶漢式單例模式的Java 實現代碼如下:

//單例模式(懶漢式)
public class Singleton { private static Singleton instance; private Singleton() {}; public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }

  在Java中,因為餓漢式實現方案天生線程安全,而懶漢式需要加號synchronized 關鍵字,影響的了性能,所以餓漢式單例性能要優子懶漢式單例。

單例模式的優點

  ① 避免了頻繁地創建和銷毀對象,減少了系統開銷。

  ② 節省了內存空間,在內存中只有一個對象。

  ③ 提供了一個全局訪問點。

單例模式的適用場景

  ✔ 針對某些需要頻繁創建對象又頻繁銷毀對象的類。

  ✔ 需要經常用到對象,但創建時消耗大量資源。

  ✔ 針對確實只能創建一個對象的情況,比如某些核心交易類,只允許保持一個對象。

單例模式代碼測試

編寫懶漢式單例模式:

public class Singleton { private static Singleton instance; private Singleton() {}; public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }

編寫main方法:

public class Test { public static void main(String[] args) { Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); if (s1 == s2) { System.out.println("s1 和 s2是同一個實例"); }else { System.out.println("s1 和 s2是不同的實例"); } } }

運行結果:

  由此可以看出,單例模式只會創建一個實例對象。

  單例模式對實例的對象做出來限制,為什么要限制呢?因為在一些情況下,當存在多個實例時,實例之間會相互影響,可能產生意想不到的Bug;但是,當我們可以確保只存在一個實例時,在這個前提下程序就可以放心的運行了。

常見問題

單例模式兩種實現方式的區別?

  單例模式主要有兩種實現方式:餓漢式和懶漢式,兩種實現方式是有區別的。

  餓漢式是在加載類時就創建了類的一個對象;而懶漢式則是在調用實例方法getInstance()才會創建一個對象。

懶漢式單例模式的劣勢?

  從多線程的角度,懶漢式單例模式是不安全的,所以為了保障線程安全,一般的實現方式是在實例創建的方法getlnstance()前加 synchronized 保障線程安全,即: public static synchronized Singleton getlnstance(){} 。這種實現方案雖然簡單,但是缺點也比較明顯;這種實現方式降低了整個實例化的性能。

如何改進懶漢式單例模式?

  不要在getlnstance()方法上進行同步,而是在方法內部進行同步。

具體操作如下:

  ① 進入方法后先檢查實例是否已經存在,如果不存在則進入同步塊;如果存在則直接返回該實例。

  ② 如果進入了同步塊,則再次檢查實例是否存在,如果不存在,就在同步塊中創建實例;如果存在則直接返回該實例。

  這種方法因為要經過兩次“檢查”,所以被稱為“雙重檢查加鎖機制”,這種方案既能實現線程安全,又能最大限度地減少性能影響。

雙重檢查加鎖機制的實現代碼:

public class Singleton { private volatile static Singleton instance = null; private Singleton() {}; public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }

  需要注意的是,雙重檢查加鎖機制用到一個關鍵字 volatile,用volatile 關鍵字修飾的變量不會被本地線程緩存,即變量的讀寫直接操作共享內存,這樣可以保證多個線程正確使用該變量。

單例模式總結

  單例模式有兩種實現方式:餓漢式(類加載時創建實例) 和 懶漢式(調用實例方法時創建實例)。

  單例模式只會創建一個實例,常用於某些核心類。

  單例模式中的餓漢式是天生線程安全的,懶漢式需要后天進行改進才會線程安全。需要注意的是懶漢式如何進行性能改進,即“雙重檢查加鎖機制”。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM