线程安全的单例模式
單例模式,即我們只允許一個類有且僅有一個實例對外提供服務(wù)。通常為了性能考慮,單例模式會以懶加載的形式創(chuàng)建,也即單例中的懶漢模式,與之相對的當(dāng)然就是單例的餓漢模式:不管用不用,我可以先給你創(chuàng)建出來。
1.單線程下的單例模式實現(xiàn)
非多線程環(huán)境下,由于不存在線程對共享數(shù)據(jù)(對象)的競爭,所以也就沒有線程安全問題,其單例模式實現(xiàn)如下:
public final class FooSingleton {//持有類的唯一實例private static FooSingleton instance = null;/*** 私有構(gòu)造函數(shù)*/private FooSingleton() {//do nothing}/*** 提供外部獲取實例的工廠方法* 非線程安全* @return*/private static FooSingleton getInstance(){if (null == instance) {//非線程安全instance = new FooSingleton();}return instance;}public void methodOther() {//類提供的實例方法,調(diào)用方獲取單例對象后,可調(diào)用類中的實例方法}}2.多線程下的單例模式實現(xiàn)
可以發(fā)現(xiàn),在多線程環(huán)境下,這個獲取單例的方法不是線程安全的:
private static FooSingleton getInstance(){if (null == instance) {//非線程安全instance = new FooSingleton();}return instance;}這就會導(dǎo)致客戶端(也就是各個調(diào)用端)獲取到的對象并不是同一個對象,這顯然就違背了我們使用單例模式的初衷了。怎樣保證線程安全呢?常用的實現(xiàn)方法有如下三種:
2-1 直接加鎖實現(xiàn)線程安全的單例模式
直接加鎖實現(xiàn)線程安全的單例模式:
public final class FooSingletonSafety {//持有類的唯一實例private static FooSingletonSafety instance = null;/*** 私有構(gòu)造函數(shù)*/private FooSingletonSafety() {//do nothing}/*** 提供外部獲取實例的工廠方法* 線程安全:加鎖實現(xiàn)* synchronized保障了原子性、可見性和有序性* @return*/private static FooSingletonSafety getInstance(){synchronized (FooSingletonSafety.class) {if (null == instance) {instance = new FooSingletonSafety();}}return instance;}public void methodOther() {//類提供的實例方法,調(diào)用方獲取單例對象后,可調(diào)用類中的實例方法}}顯然,直接加鎖是可以保證單例模式的線程安全的,但是你可能會發(fā)現(xiàn),這種實現(xiàn)方式下,每個線程在獲取單例對象的時候都要去搶鎖,獲取之后再釋放鎖,這顯然效率是不高的(每次獲取對象都要有鎖開銷)。所以,一般我們會通過下面的DCL去實現(xiàn)線程安全的單例模式
2-2 DCL實現(xiàn)線程安全的單例模式
DCL,即double-check-locking雙重檢查鎖定,代碼實現(xiàn)如下:
public final class FooSingletonBasedDCL {//持有類的唯一實例private static volatile FooSingletonBasedDCL instance = null;/*** 私有構(gòu)造函數(shù)*/private FooSingletonBasedDCL() {//do nothing}/*** 提供外部獲取實例的工廠方法* 線程安全:DCL實現(xiàn)* synchronized保障了原子性、可見性和有序性* volatile保證校驗時(null == instance) instance對各個線程是可見的,同時禁止JIT和處理器重排序* @return*/private static FooSingletonBasedDCL getInstance(){if (null == instance) {synchronized (FooSingletonSafety.class) {if (null == instance) {//new關(guān)鍵字對應(yīng)三條指令:1.分配對象所需的存儲空間 2.實例化對象 3.將對象引用賦值給變量//這三條指令正常執(zhí)行順序是1->2->3,但是JIT編譯器或處理器在執(zhí)行時可能會將其優(yōu)化為1->3->2//所以,為了保證instance的可見性以及禁止重排序,所以instance變量必須要用volatile修飾//否則,極端情況下,一個線程在判斷if (null == instance)時,instance不為空直接返回,但此時//instance可能是個半對象(對象并沒有經(jīng)過構(gòu)造方法初始化),這將會造成代碼錯誤instance = new FooSingletonBasedDCL();}}}return instance;}public void methodOther() {//類提供的實例方法,調(diào)用方獲取單例對象后,可調(diào)用類中的實例方法}}DCL你可能開發(fā)中基本沒用過,但是這種實現(xiàn)方式在很多開源框架如Spring、dubbo等中都有使用
2-3 利用類加載機制實現(xiàn)線程安全的單例模式
利用Java的類加載機制:加載->鏈接->初始化,我們可以輕易的實現(xiàn)線程安全的單例模式,這種單例模式形式最簡單,代碼實現(xiàn)如下:
public final class BarSingleton {//利用類初始化只會發(fā)生一次,創(chuàng)建線程安全的單例private static final BarSingleton INSTANCE = new BarSingleton();public static BarSingleton getInstance() {return INSTANCE;}public void methodOther() {//類提供的實例方法,調(diào)用方獲取單例對象后,可調(diào)用類中的實例方法} }總結(jié)
- 上一篇: 子承父业-C#继承
- 下一篇: HDU 1159 Common Subs