相親網(wǎng)站怎么做企業(yè)線上培訓(xùn)平臺有哪些
單例模式是設(shè)計模式之一,能保證某個類在程序中只存在唯一一份實例,而不會創(chuàng)建出多個實例
單例模式的具體實現(xiàn)方法有很多,最常見的是 “餓漢” 和 “懶漢” 兩種。
餓漢模式
class Singlenton{private static Singlenton instance = new Singlenton();public static Singlenton getInstance(){return instance;}//在此類的外面無法調(diào)用構(gòu)造方法,無法創(chuàng)建實例private Singlenton(){}
}
懶漢模式
類加載的時候不創(chuàng)建實例,第一次使用的時候才創(chuàng)建實例
單線程版
class Singletonlazy{private static Singletonlazy instance = null;public static Singletonlazy getInstance(){if(instance==null){instance = new Singletonlazy();}return instance;}private Singletonlazy(){}
}
多線程版
相比單線程版,多線程版考慮了線程安全問題
線程安全問題發(fā)生在首次創(chuàng)建實例時,可能多個線程同時調(diào)用getInstance方法,就可能導(dǎo)致創(chuàng)建了多個實例。
加上 synchronized 可以改善線程安全問題
class Singletonlazy{private static Singletonlazy instance = null;private static Object locker = new Object();public static Singletonlazy getInstance(){synchronized (locker){if(instance==null){instance = new Singletonlazy();}}return instance;}private Singletonlazy(){}
}
多線程版優(yōu)化
上面的代碼雖然說解決了線程安全問題,但是只要調(diào)用了getInstance方法,就會觸發(fā)加鎖操作,產(chǎn)生阻塞,影響性能。
我們想要優(yōu)化,就要在加鎖之前判定一下是否需要加鎖。
外層的if(instance==null)是判斷實例有沒有創(chuàng)建
內(nèi)層的if(instance==null)進一步判斷實例有沒有創(chuàng)建,因為在外層 if 和加鎖之間,切換了線程并創(chuàng)建了實例,此時切換到原來的線程如果沒有判斷,就會創(chuàng)建出多個實例。
但是光加了一個外層 if 還不夠,此時可能因為指令重排序引起的線程安全問題
instance = new Singletonlazy();分為三條指令
- 分配內(nèi)存空間
- 執(zhí)行構(gòu)造方法
- 內(nèi)存空間的地址賦值給引用變量
編譯器可能按照 1 2 3 的順序來執(zhí)行,也可能按照 1 3 2 的順序執(zhí)行
當(dāng)按照 1 3 2的順序執(zhí)行時,由于 3 是把內(nèi)存空間的地址賦值給引用變量,所以此時 instance現(xiàn)在不為 null 了,此時如果其他線程判斷外層 if 時,由于instance不為null了,所以直接返回instance,但是此時instance指向沒有初始化,上面值全是0的內(nèi)存,此時getInstance到的就是個錯誤的值,會引發(fā)一系列不可預(yù)期的情況。
此時,我們用volatile 關(guān)鍵字告知編譯器此變量指令不可重排序即可解決。
class Singletonlazy{private static volatile Singletonlazy instance = null;private static Object locker = new Object();public static Singletonlazy getInstance(){if(instance==null){synchronized (locker){if(instance==null){instance = new Singletonlazy();}}}return instance;}private Singletonlazy(){}
}