單例模式是一種常用的軟件設計模式,通過它能夠保證系統中,應用該模式的一個類只有一個實例。
在面試中,本人總共手寫過三種單例模式:懶漢模式、餓漢模式、雙重檢查模式
懶漢模式:顧名思義,當系統需要類的實例的時候纔會進行初始化,否則就不會進行初始化。
public class Singleton {
private static Singleton singleton = null;
private Singleton() {}
public synchronized static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
餓漢模式:餓漢就是無論系統需不需要,都會準備好該實例
public class Singleton {
private final static Singleton singleton = new Sinleton();
private Singleton() {}
public static Singleton getInstance() {
return singleton;
}
}
雙重檢查模式:
public class Singleton {
private volatile static Singleton singleton = null;
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
這個代碼就像進門一樣,那麼你先推開門看看是否可以打開,如果可以打開的話,那麼你就進去,然後上鎖,最後在檢查一遍是否鎖好。
但是本人覺得這個雙重檢查的重心應該實在 volatile 關鍵字上,如果沒有加 volatile 關鍵字,那麼在多線程的環境下就可能會調用失敗。
參考:https://blog.csdn.net/xiaobudian0381/article/details/91403670
一個類進行實例化的時候簡單的來說要進行三步:
- 分配內存空間
- 初始化對象
- 返回該對象的地址
在第二步和第三步中不存在依賴關係,所以可能會發生指令重排的事情,舉例說明:A線程調用 getInstance() 方法,發生了指令重排(第二步 第三部 顛倒),先返回的是該對象的地址。此時,線程B調用 getInstance() 方法,發現 singleton 不是空,直接返回。問題出現了,A線程實例化的時候僅僅返回的是對象的地址,但是並沒有進行初始化,但是線程B不知道,所以出現了錯誤。
但是加了 volatile 關鍵字就可以避免這種情況。關於其他的 volatile 關鍵字的講解,可以參考:
https://blog.csdn.net/xiaobudian0381/article/details/90897800
https://blog.csdn.net/xiaobudian0381/article/details/91348251