歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Java中的單例模式詳解

Java中的單例模式詳解

日期:2017/3/1 9:10:30   编辑:Linux編程

概念:

Java中的單例模式是一種常見的設計模式,單例模式的寫法有好幾種這裡主要介紹三種:懶漢式單例,餓漢式單例,登記式單例(這種最好)。

單例模式的特點:

1.單例類只有一個實例

2.單例類必須自己創建自己的唯一實例

3.單例類必須給其他的對象提供這一實例

單例模式確保某個類只有一個實例,而且自行實例化並向整個系統提供這個實例。在計算機系統中,線程池、緩存、日志對象、對話框、打印機、顯卡的驅動程序對象常被設計成單例。這些應用都或多或少具有資源管理器的功能。每台計算機可以有若干個打印機,但只能有一個Printer Spooler,以避免兩個打印作業同時輸出到打印機中。每台計算機可以有若干通信端口,系統應當集中管理這些通信端口,以避免一個通信端口同時被兩個請求同時調用。總之,選擇單例模式就是為了避免不一致狀態,避免政出多頭。

1.懶漢式單例(經典的單例模式)

/*
* 經典單例模式 ,在第一次調用getInstance()的時候實例化自己
*/
public class Singleton {
private static Singleton singleton=null;
private Singleton (){}
public static Singleton getInstance(){
if(singleton==null)
return new Singleton();
else return singleton;
}
}
Singleton通過將構造方法限定為private避免了類在外部被實例化,在同一個虛擬機范圍內,Singleton的唯一實例只能通過getInstance()方法訪問。

(事實上,通過Java反射機制是能夠實例化構造方法為private的類的,那基本上會使所有的Java單例實現失效。此問題在此處不做討論,姑且掩耳盜鈴地認為反射機制不存在。)


但是以上懶漢式單例的實現沒有考慮線程安全問題,它是線程不安全的,並發環境下很可能出現多個Singleton實例.


例如,線程A和線程B同時第一次運行到如下語句

if (instance == null){ ...}此時2個線程的判斷都為true,所以就都進行了一次實例化new Singleton ()。

要實現線程安全,有以下三種方式,都是對getInstance這個方法改造,保證了懶漢式單例的線程安全。

1、在getInstance方法上加同步

public static synchronized Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return singleton;
}

2.添加塊鎖(雙重鎖檢查)


public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
} else
return singleton;
}

餓漢單例模式

/*
* 餓漢一步到位方式
*/
public class Singleton<span >1{ </span>
private static final Singleton1 instance = new Singleton2();
private Singleton1(){};
public static Singleton getInstance(){
return instance;
}
}

餓漢式在類創建的同時就已經創建好一個靜態的對象供系統使用,以後不再改變,所以天生是線程安全的。

餓漢式和懶漢式區別

從名字上來說,餓漢和懶漢,

餓漢就是類一旦加載,就把單例初始化完成,保證getInstance的時候,單例是已經存在的了,

而懶漢比較懶,只有當調用getInstance的時候,才回去初始化這個單例。

1、線程安全:

餓漢式天生就是線程安全的,可以直接用於多線程而不會出現問題,

懶漢式本身是非線程安全的,為了實現線程安全有幾種寫法,分別是上面的1、2,這三種實現在資源加載和性能方面有些區別。

第1種,在方法調用上加了同步,雖然線程安全了,但是每次都要同步,會影響性能,畢竟99%的情況下是不需要同步的,

第2種,在getInstance中做了兩次null檢查,確保了只有第一次調用單例的時候才會做同步,這樣也是線程安全的,同時避免了每次都同步的性能損耗

登記式模式

(holder)
?public class Singleton {
private Singleton() {} //構造方法是私有的,從而避免外界利用構造方法直接創建任意多實例。
public static Singleton getInstance() {
return Holder.SINGLETON;
}
private static class Holder {
private static final Singleton SINGLETON = new Singleton();
}
}

三種模式的優缺點

懶漢式:

優點:第一次調用才初始化,避免內存浪費。

缺點:必須加鎖synchronized 才能保證單例,(如果兩個線程同時調用getInstance方法,會chuxia)但加鎖會影響效率。

餓漢式:
優點:沒有加鎖,執行效率會提高。

缺點:類加載時就初始化,浪費內存。

登記:內部類只有在外部類被調用才加載,產生SINGLETON實例;又不用加鎖。此模式有上述兩個模式的優點,屏蔽了它們的缺點,是最好的單例模式。

Copyright © Linux教程網 All Rights Reserved