單例模式
單例模式,顧名思義,在程序運行中,實例化某個類時只實例化一次,即只有一個實例對象存在。例如在古代,一個國家只能有一個皇帝,在現代則是主席或總統等。
在Java語言中單例模式有以下實現方式
1.餓漢式
import org.junit.jupiter.api.Test; public class Singleton { //靜態成員變量 private static Singleton singleton = new Singleton(); private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } //構造函數私有化 private Singleton(){ } //返回靜態資源中Singleton實例 public static Singleton getInstance() { return singleton; } /** * 單元測試 */ @Test public void testSingleton() { //通過調用類的靜態方法返回類的實例對象 Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); s1.setName("zhangsan"); s2.setName("lisi"); System.out.println(s1.getName()); System.out.println(s2.getName()); } }
在類加載時,直接將實例對象初始化,並且該實例是靜態的,屬於類的成員變量,通過調用類的靜態方法返回該對象。
運行testSingleton單元測試,輸出的兩行都是lisi,因為s1,s2指向的同一個實例對象,這個對象在類創建的時候就存在了。
餓漢式是線程安全的,不管系統的那一個線程獲取這個對象,他們都是該類同一個對象。缺點是在程序在一開始就創建了該對象,占用內存空間。下面這種實現方式增加判斷,在程序調用時才實例化該對象。
import org.junit.jupiter.api.Test; public class Singleton { //不初始化實例對象 private static Singleton singleton = null; private String name; public String getName() { return name; } //構造函數私有化 private Singleton(){ } public void setName(String name) { this.name = name; } //當被調用時才動態實例化 public static Singleton getInstance() { if(singleton == null) { singleton = new Singleton(); } return singleton; } }
懶漢式解決了餓漢式的實例對象初始化占用內存的情況,但是懶漢式存在線程安全的問題,當多個線程同時訪問getInstance()方法時,有可能在第一個線程進入if語句是還沒new Singleton()時,這時第二個線程判斷if的時候就會為真,則會創建新的實例,單例模式設計失敗。
這時需要在getInstance()方法上添加synchronized修飾符,表示線程同步,即當一個線程訪問該方法時,其他線程需要等待其釋放資源。
public static synchronized Singleton getInstance() { if(singleton == null) { singleton = new Singleton(); } return singleton; }
public static Singleton getInstance() { if(singleton == null) { synchronized (Singleton.class) { if(singleton==null) { singleton = new Singleton(); } } } return singleton; }
這里將synchronized放到判斷語句里面,這樣當第一個線程調用getInstance()方法時,singleton為空,進入synchronized代碼塊,其他的線程即使也判斷singleton為空,則需要等待第一個線程完成資源的使用,這樣保證了線程的安全。
當實例化對象完成時,其他的線程調用getInstance()方法時,直接返回Singleton對象即可,不用再等待,這一點優化了系統,提高了系統的性能。
4.總結
模式來源於生活。本文用Java語言實現了單例模式。三種實現方式都是線程安全的,餓漢式比較占用內存空間;懶漢式則降低了系統的性能;雙重檢測是懶漢式的升級,即能保證線程的安全,也能以懶加載的方式實現單例模式,在大規模系統中節省對象的創建時間,提高系統的性能。在系統中共享同一個資源或同一個對象,則可利用單例模式來實現。