前言
java 常見(jiàn)的單例模式有三種:
- 懶漢: getInstance的時(shí)候?qū)嵗?
- 餓漢: 引用AA類的時(shí)候?qū)嵗? 例如 AA.fun() 或者 AA.getInstance();
- 靜態(tài)內(nèi)部類: getInstance的時(shí)候?qū)嵗? 寫(xiě)法比懶漢要簡(jiǎn)單;
個(gè)人理解:
如果沒(méi)有除了getInstance 方法之外的 public static fun 的話, 以上三種單例模式在加載時(shí)間上基本是沒(méi)有差別的. 考慮到實(shí)現(xiàn)比較輕松, 推薦靜態(tài)內(nèi)部類方式創(chuàng)建單例.
java 靜態(tài)內(nèi)部類模式
public class AA {
private static class Holder{
private static AA instance = new AA();
}
private AA(){}
public static AA getInstance(){
return Holder.instance;
}
}
java 懶漢模式
-
比較簡(jiǎn)單的寫(xiě)法是這樣:
public class AA { private static AA instance; public static AA getInstance(){ if(instance == null){ AA = new AA(); } return instance; } }
-
但上面這樣寫(xiě)不是線程安全的, 如果多線程同時(shí)調(diào)用 getInstance 時(shí), 有可能同時(shí)通過(guò) instance==null 的判斷, 導(dǎo)致執(zhí)行兩遍 new AA , 所以需要加鎖, 改進(jìn)寫(xiě)法:
public class AA { private static AA instance; public static AA getInstance(){ if(instance == null){ synchronized(AA.class){ if(instance == null){ AA = new AA(); } } } return instance; } }
-
但這樣還是有漏洞, jvm 有指令重排機(jī)制, 臨界情況下的靠后者有可能會(huì)得到一個(gè)初始化尚未完成的 instance 對(duì)象, 這時(shí)需要加 volatile 修飾符, 這個(gè)修飾符能組織變量訪問(wèn)前后的指令重排, 改進(jìn)寫(xiě)法:
public class AA { private volatile static AA instance; public static AA getInstance(){ if(instance == null){ synchronized(AA.class){ if(instance == null){ AA = new AA(); } } } return instance; } }
kotlin 的單例模式
-
惡漢 --> 調(diào)用AA.getInstance().method()
class AA private Constructor(){ fun method(){ // ... } companion object{ @JvmStatic val instance: AA = AA() } }
-
懶加載 --> 調(diào)用AA.getInstance().method()
class AA private Constructor(){ fun method(){ // ... } companion object{ @JvmStatic val instance: AA by lazy { AA() } } }
-
極簡(jiǎn)單例 (也是懶加載)--> 調(diào)用AA.INSTANCE.method().
ps 在新版本里不能AA.INSTANCE, 直接AA.method()object AA{ fun method(){ // ... } }
ps: 雖然 kotlin 的 lazy 文檔里提到 返回的是線程安全對(duì)象, 但我沒(méi)有測(cè)試(手動(dòng)滑稽)