1. 雙重加鎖
public class Singlnton{
private static volatile Singlnton instance;
private Singlnton(){
}
public static Singlnton getInstance(){ // 1
if(instance==null){ // 2
synchronized(Singlnton.class){ // 3
if(instance==null){ // 4
instance = new Singlnton(); // 5
}
}
}
return instance;
}
}
- volatile關鍵字的作用?
- 雙重檢鎖單例模式在CPU的工作流,主要分爲三步,1:分配內存對象空間。2:初始化對象。3:設置instance執行剛纔分配的內存地址,注意jvm和cpu優化會指令重排,上面順序會變成1-3-2,單線程環境下,此順序是沒有問題,2和3 前後沒有依賴性,但是在多線程情況下會有這種情況,當線程A在執行第5行代碼時,B線程進來執行到第2行代碼。假設此時A執行的過程中發生了指令重排序,即先執行了1和3,沒有執行2。那麼由於A線程執行了3導致instance指向了一段地址,所以B線程判斷instance不爲null,會直接跳到第6行並返回一個未初始化的對象。volatile保持指令的有序性,能夠有效禁止指令重排序。
2. 靜態內部類
public class Singlnton{
private Singlnton(){
}
private static class SinglntonHolder{
private static Singlnton INSTANCE = new Singlnton();
}
public static Singlnton getInstance(){
return SinglntonHolder.INSTANCE;
}
}
3. Kotlin中的單例模式
class Singlnton{
companion object{
val instance : Singlnton by lazy { Singlnton() }
}
}
- lateinit和by lazy的區別:
- lateinit只能用於修飾變量var,不能用於可空的屬性和Java的基本類型。
- lateinit可以在任何位置初始化並且可以初始化多次。
- lazy()只能用於修飾常量val,並且lazy()是線程安全的。
- lazy()是一個函數,可以接受一個Lambda表達式作爲參數,第一次調用時會執行Lambda表達式,以後調用該屬性會返回之前的結果。
- lazy()源碼分析