面試官:請手寫下幾種常見的單例模式
我:好的(面帶微笑),心里暗喜(送分題)。
沒成想提筆便寫出了如此豪放的代碼,不堪回首,請原諒我的不羈!
此篇整理了幾種常見的單例模式代碼示例,再有面試官讓手撕單例模式,便能心中有碼,下筆有神。
為什么要有單例模式
實際編程應用場景中,有一些對象其實我們只需要一個,比如線程池對象、緩存、系統全局配置對象等。這樣可以就保證一個在全局使用的類不被頻繁地創建與銷毀,節省系統資源。
實現單例模式的幾個要點
- 首先要確保全局只有一個類的實例。
要保證這一點,至少類的構造器要私有化。 - 單例的類只能自己創建自己的實例。
因為,構造器私有了,但是還要有一個實例,只能自己創建咯! - 單例類必須能夠提供自己的唯一實例給其他類
就是要有一個公共的方法能返回該單例類的唯一實例。
單例模式的6種實現
1、餓漢式—靜態常量方式(線程安全)
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
類加載時就初始化實例,避免了多線程同步問題。天然線程安全。
2、餓漢式—靜態代碼塊方式(線程安全)
其實就是在上面 靜態常量餓漢式 實現上稍微變動了一下,將類的實例化放在了靜態代碼塊中而已。其他沒區別。
public class Singleton {
private static Singleton instance;
static {
instance = new Singleton();
}
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
3、懶漢式(線程不安全)
public class Singleton {
private static Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
這是最基本的實現方式,第一次調用才初始化,實現了懶加載的特性。多線程場景下禁止使用,因為可能會產生多個對象,不再是單例。
4、懶漢式(線程安全,方法上加同步鎖)
public class Singleton {
private static Singleton singleton;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
和上面 懶漢式(線程不安全)實現上唯一不同是:獲取實例的getInstance()方法上加了同步鎖。保證了多線程場景下的單例。但是效率會有所折損,不過還好。
5、雙重校驗鎖(線程安全,效率高)
public class Singleton {
private volatile static Singleton singleton;
private Singleton() {}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
此種實現中不用每次需要獲得鎖,減少了獲取鎖和等待的事件。
注意volatile關鍵字的使用,保證了各線程對singleton靜態實例域修改的可見性。
6、靜態內部類實現單例(線程安全、效率高)
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
這種方式下 Singleton 類被裝載了,instance 不一定被初始化。因為 SingletonHolder 類沒有被主動使用,只有通過顯式調用 getInstance 方法時,才會顯式裝載 SingletonHolder 類,從而實例化 instance。
注意內部類SingletonHolder要用static修飾且其中的靜態變量INSTANCE必須是final的。
此篇完。單例模式掌握至此,足以應付“手寫單例”的面試場景。
掃碼關注微信公眾號:二營長的筆記。回復“二營長”,可領取Java相關技術資料。