什麼是單例模式
單例就是單個對象的意思,指在系統運行期間,一個類最多隻能創建一個對象,且該類能自行創建這個對象的一種編碼設計模式。
單例模式有兩個特點:
- 在系統的整個生命週期內,單例對象最多隻能有一個
- 單例對象必須由單例類自行創建,並對外提供訪問入口
一個類可以創建多個對象,這是面向對象的語言特性,想要實現單例模式,就要屏蔽這個特性,防止系統可以隨意創建類的對象。
要做到這一點,通常做法就是利用private
關鍵字將類的構造方法私有化,使外部調用者無法利用new
關鍵字創建類的對象。一旦私有化了類的構造方法,就意味着能夠使用new
關鍵字創建對象的權利只有該類自己擁有!所以該類就必須自己創建單例,並對外界提供可訪問該單例對象的靜態方法。
爲什麼是靜態方法?因爲外界不可能創建單例類的對象,也就沒有可能直接調用對象的方法。再進一步可推測出單例對象也要用static
關鍵字修飾,因爲靜態方法中無法訪問對象成員變量。
單例模式的實現
餓漢模式
單例類加載或者系統啓動的時候,就創建好單例對象,不管後面會不會使用。
public class Singleton {
private static Singleton singleton = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return singleton;
}
}
餓漢模式不存在線程安全問題,但是也要注意在類加載、系統啓動的時候會不會有其它問題。
懶漢模式
系統啓動運行期間不創建單例對象,只在第一次調用單例類入口方法時纔去創建單例對象。
特點:不使用不創建,使用時才創建,儘可能延遲單例對象的實例化。
具體代碼實現又可分爲兩大應用場景:線程不安全、線程安全。
線程不安全
public class Singleton {
private static Singleton singleton;
private Singleton() {
}
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
線程安全
保證線程安全的第一種方式:簡單粗暴,直接使用synchronized
靜態方法。
public class Singleton {
private static Singleton singleton;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
保證線程安全的第二種方式:雙重加鎖檢查DCL(Double Check Lock)
。
public class Singleton {
// volatile關鍵字必須有
private volatile static Singleton singleton;
private Singleton() {
}
public static Singleton getInstance() {
if (singleton == null) {// 第一次校驗
synchronized (Singleton.class) {// 加鎖
if (singleton == null) {// 第二次校驗
singleton = new Singleton();
}
}
}
return singleton;
}
}
雙重加鎖檢查的要點有兩個:
- 單例對象需要使用
volatile
修飾,保證單例對象完全初始化後才能被訪問到 - 加鎖前後,兩次檢查單例對象是否已被創建,防止多次重複創建
保證線程安全的第三種方式:靜態內部類
public class Singleton {
private static class Holder {
private static Singleton singleton = new Singleton();
}
private Singleton() {
}
public static Singleton getInstance() {
return Holder.singleton;
}
}
這種方式特點就是不需要加鎖,利用內部類延時加載機制達到單例類的延時創建效果,值得推薦和使用!