大家都知道,一般實現單例有兩種寫法: 餓漢式 和 懶漢式, 餓漢式是線程安全的,在編譯期間已完成初始化,加載到了內存裏。 懶漢式一般寫法是非線程安全的, 那懶漢式的線程安全單例應該如何實現呢,以及如何寫出低耗能的線程安全單例呢 ?
單例實現關鍵點
- 構造函數私有,private
- 實例對象 靜態 且 私有
- 公開獲取實例的靜態方法
下面我們直接上代碼了,按重要程度排序,提供三種方式:
一、高性能的線程安全單例 - 懶漢式
關鍵注意點:
- volatile 關鍵字修飾實例對象, 禁止指令重排序
- 加鎖實例初始化過程
- 判斷實例對象爲空時,進行雙重校驗
package safe;
/**
* @description 線程安全的單例——懶漢式加載
* @author [email protected]
* @date 2018/11/20
*/
public class LazySingleton {
/**
* volatile 修飾屬性,簡直指令重排序
*/
private static volatile LazySingleton lazySingleton = null;
private LazySingleton() {
//模擬: 創建線程爲耗時操作
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static LazySingleton getInstance() {
// 雙重校驗
if (null == lazySingleton) {
synchronized(LazySingleton.class) {
if (lazySingleton == null) {
lazySingleton = new LazySingleton();
}
}
}
return lazySingleton;
}
public static void main(String args[]) {
for (int i = 0; i < 10; i ++) {
new Thread(() -> {
System.out.println(LazySingleton.getInstance());
}).start();
}
}
}
二、線程安全單例 - 懶漢式
同樣是懶漢式,但是這次實現的方式不一樣,我們直接選擇在 獲取實例的方法上,加上同步鎖, 但是缺點就是有點消耗性能。
package safe;
/**
* @description 線程安全的單例-懶漢-消耗性能
* 將 Sychronized 關鍵字加載方法上,消耗性能
* @author [email protected]
* @date 2018/11/20
*/
public class LazySingleton_SyncMethod {
private LazySingleton_SyncMethod() {}
private static LazySingleton_SyncMethod instance = null;
public static synchronized LazySingleton_SyncMethod getInstance() {
if (instance == null) {
instance = new LazySingleton_SyncMethod();
}
return instance;
}
public static void main(String args[]) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
System.out.println(LazySingleton_SyncMethod.getInstance());
}).start();
}
}
}
三、線程安全單例-餓漢式
編譯期間,直接完成了初始化。
package safe;
/**
*
* @description 餓漢單例
* @author [email protected]
* @date 2018/11/20
*/
public class HungerSingleton {
private static HungerSingleton ourInstance = new HungerSingleton();
public static HungerSingleton getInstance() {
return ourInstance;
}
private HungerSingleton() {
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(() -> {
System.out.println(HungerSingleton.getInstance());
}).start();
}
}
}