/**
* 方式一
* instance 單例初始值是null,還未構(gòu)建,則構(gòu)建單例對象并返回;是懶漢模式 - 線程不安全
* instance 單例對象一開始就被new 出來,主動構(gòu)建,則不需要判空操作;是餓漢模式 - 線程安全
*/
private ZpDanLiDemo(){}
private static ZpDanLiDemo instance = null;
public static ZpDanLiDemo getInstance() {
// (場景1:ZpDanLIDemo剛被初始化,線程1、2兩個線程同時調(diào)用 getInstance 方法,
// 所以instance為空,兩個線程同時通過條件判斷,對象創(chuàng)建了兩次)
if (instance == null) {
instance = new ZpDanLiDemo();
}
return instance;
}
注:這種方式是線程不安全的,具體原因可以看注釋描述。
懶漢與餓漢兩種單例模式總是傻傻分不清,應用與面試的時候要多注意。
/**
* 方式二
*/
private ZpDanLiDemo(){}
// volatile 對象new的時候,JVM執(zhí)行順序保證正常執(zhí)行
private volatile static ZpDanLiDemo instance = null;
public static ZpDanLiDemo getInstance() {
// 雙重檢測機制
if (instance == null) {
// 同步鎖 (為了對象不被 new 多次,使用同步鎖,鎖住整個類)
synchronized (ZpDanLiDemo.class) {
// 雙重檢測機制 (進入synchronized臨界區(qū)以后,還要再做一次判空。
// 因為當兩個線程同時訪問的時候,線程A構(gòu)建完對象,線程B也已經(jīng)通過
// 了最初的判空驗證,不做第二次判空的話,線程B還是會再次構(gòu)建instance對象)
if (instance == null) {
instance = new ZpDanLiDemo();
}
}
}
return instance;
}
注:這種方式是線程安全
/**
* 方式三 靜態(tài)內(nèi)部類實現(xiàn)單例模式
* 從外部是無法訪問靜態(tài)內(nèi)部類lazyHolder,只有當調(diào)用getInstance方法的時候,才能得到單例對象。
* instance 對象初始化的時機并不是在單例類ZpDanLiDemo被加載的時候,而是在調(diào)用
* getInstance方法,使得靜態(tài)內(nèi)部類LazyHolder被加載的時候。
* 因此這種實現(xiàn)方式是利用classLoader的加載機制來實現(xiàn)懶加載,并保證構(gòu)建單例的線程安全。
*/
private ZpDanLiDemo(){}
private static class LazyHolder {
private static final ZpDanLiDemo instance = new ZpDanLiDemo();
}
public static ZpDanLiDemo getInstance() {
return LazyHolder.instance;
}
注:線程安全
使用靜態(tài)內(nèi)部類構(gòu)建單例,事件比較靠譜的一件事兒。個人喜好,是比較喜歡用這種方式。
使用反射機制打破單例
/**
* 利用反射打破單例
* 使用枚舉可以防止反射構(gòu)建
*/
private void getDanLi() {
try {
// 獲得構(gòu)造器
Constructor con = ZpDanLiDemo.class.getDeclaredConstructor(ZpDanLiDemo.class);
// 設(shè)置為可訪問
con.setAccessible(true);
// 構(gòu)造兩個不同的對象
ZpDanLiDemo zpDanLiDemo1 = new ZpDanLiDemo();
ZpDanLiDemo zpDanLiDemo2 = new ZpDanLiDemo();
// 驗證是否是不同對象 (log - > false)
Log.e("zpan", "====" + zpDanLiDemo1.equals(zpDanLiDemo2));
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}