前言 : 顧名思義, 單例就是說一個類只有一個實例, 什麼場景大家自己可以思考一下 .
單例模式
這裏介紹單例的實現方式中的兩種 : 懶漢式, 餓漢式 .
1. 懶漢式
何爲懶, 就是說沒調用它的時候它不給你創建這個實例, 需要用到的時候纔開始創建, 即爲 '懶' .
上代碼 :
public class Singleton {
private static Singleton singleton;
private Singleton() { }
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
分析 : 以上實現方式, 在第一次調用類的 getInstance 方法時, 創建對象實例, 之後調用直接返回這個類實例, 靜態的標記爲類屬性, 只有一個副本, 因此爲單例 .
但是這種實現不是線程安全的, 併發時, 可能出現多個實例的時候, 我們通過幾種方式可以將其改造爲線程安全的, ①爲getInstance方法加同步 ②'懶'同步 ③靜態內部類工廠
具體實現看代碼 :
① getInstance方法加同步
public class Singleton {
private static Singleton singleton;
private Singleton() { }
public static synchronized Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
分析 : 併發操作時, 只有一個線程會得到 getInstance 方法, 避免了併發創建多個實例的情況, 但是每次調用 getInstance 方法都會同步加鎖, 而實際上一旦單實例創建後, 這些鎖操作都是不必要的, 性能就產生了問題, 因此可以改爲下面這種寫法 .
② '懶' 同步
public class Singleton {
private static Singleton singleton;
private Singleton() { }
public static Singleton getInstance() {
if (singleton == null) {
synchronized (singleton) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
分析 : 這種做法採取了雙重檢查鎖定的做法, 避免了每次都同步加鎖的性能問題, 同時也是線程安全的 .
③ 靜態內部工廠類
public class Singleton {
private Singleton() { }
public static Singleton getInstance() {
return SingletonFactory.INSTANCE;
}
private static class SingletonFactory {
private static final Singleton INSTANCE = new Singleton();
}
}
分析 : 利用 classloader 的機制, 保證只有一個實例, 而且可以做到懶加載的效果(內部類只有在第一次被使用時纔會加載) .
2. 餓漢式單例模式
何爲餓? 即非常飢渴, 非常需要, 等不到你調用, 就非常急切的將實例創建出來 . 實際上實現方式, 跟懶漢式的第三種 靜態內容工廠類比較相似, 利用類加載機制, 即靜態屬性只加載一次並且提前加載 .
直接上代碼 :
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() { }
public static Singleton getInstance() {
return Singleton.INSTANCE;
}
}
分析 : 這種方式, 天然就是線程安全的, 但不能做到需要時才創建這個效果.