知識積累 通過單例模式學習volatile關鍵字

2021-10-06 04:16:34 字數 2448 閱讀 1064

/**

* 單例模式的實現

*/public class singleton

public static singleton getsingleton()}}

return singleton;

}}

上例就是著名的單例模式的雙檢索實現。

在單執行緒情況下,並不會有什麼問題,但是在多執行緒情況下,就會有問題。

會有什麼問題呢?

在多執行緒環境下,乙個執行緒在執行到第一次檢查後,singleton不為空,但是此時的singleton還沒有完成初始化,因為singleton = new singleton();這一步是分為三步去完成的。

1、分配物件記憶體空間

2、初始化物件

3、設定instance指向剛分配的記憶體位址,此時instance!=null

假如此時發生重排序了,順序就變成如下的情況:

1、分配物件記憶體空間

2、設定instance指向剛分配的記憶體位址,此時instance!=null

3、初始化物件

為什麼會發生這種情況?什麼是重排序?一臉懵逼。。。

因為2和3不存在資料依賴的關係,在單執行緒情況下,不管是在前面還是在後面,都不會影響程式的結果,這種重排序優化是允許的。當多執行緒情況下,有乙個執行緒訪問時發現instance!=null,但是instance還沒初始化完成,這就造成了執行緒的安全性問題。

那怎麼解決呢?

很簡單,加上volatile關鍵字。那這個關鍵字做什麼用的?

volatile:禁止指令重排序

/**

* 單例模式的實現

*/public class singleton

public static singleton getsingleton()}}

return singleton;

}}

概念:記憶體屏障(memory barrier),乙個cpu指令。

作用:1、保證特定操作的執行順序;

2、保證某些變數的記憶體可見性。

volatile變數正是通過記憶體屏障實現其在記憶體中的語義(可見性和禁止重排優化)。

當寫乙個volatile變數時,jmm會把該執行緒對應的工作記憶體中的共享變數重新整理到主記憶體中;

當讀乙個volatile變數時,jmm會把該執行緒對應的工作記憶體置為無效,那麼該執行緒將只能從主記憶體中重寫讀取共享變數。

volatile:jvm提供的輕量級同步機制

volatile關鍵字有如下兩個作用:

1、保證被volatile修飾的共享變數對所有執行緒總是可見的,即當乙個執行緒修改了乙個被volatile修飾的共享變數的值的時候,其他執行緒立即感知到變動;

2、禁止指令重排序優化。

關於volatile的可見性作用,我們必須意識到被volatile修飾的變數,對所有執行緒總是立即可見的。對volatile變數的所有寫操作,總是能理解反應到其他執行緒中,但是對於volatile變數運算操作在多執行緒環境中並不保證安全性。

public class volatiledemo 

}

value++並不是乙個原子性操作,value++是先讀取值,然後再寫回乙個新值,相當於原來的值加上1,分兩步來完成。如果第二個執行緒在第乙個執行緒讀取舊址和寫回新值期間,讀取value的閾值,那麼第二個執行緒就會與第乙個執行緒一起看到同乙個值,並執行相同的加1操作,也就引發了執行緒安全問題。因此對於increase方法必須使用synchronized修飾,以便保證執行緒安全。需要補充,並且注意的是,synchronized關鍵字解決的是執行控制的問題,它會阻止其他執行緒獲取當前物件的監控鎖,這就使得當前物件中被synchronized關鍵字保護的**塊無法被其他執行緒訪問,也就無法併發執行。

public class volatiledemo 

}

一旦使用synchronized修飾方法之後,由於synchronized本身也具有與volatile相同的特性(即可見性),因此在這樣的情況下,就完全可以省去volatile修飾變數

public class volatiledemo 

}

1、volatile本質是在告訴jvm當前變數在暫存器(工作記憶體)中的值是不確定的,需要從主存中讀取;synchronized則是鎖定當前變數,只有當前執行緒可以訪問該變數,其他執行緒被阻塞住直到該執行緒完成變數操作為止。

2、vlotaile僅能使用在變數級別;synchronized則可以使用變數、方法和類級別。

3、volatile僅能實現變數的修改可見性,不能保證原子性;而synchronized則可以保證變數修改的可見性和原子性。

4、volatile不會造成執行緒的阻塞;synchronized可能會造成執行緒的阻塞。

5、volatile標記的變數不會被編譯器優化;synchronized標記的變數可以被編譯器優化。

通過容器實現單例模式

今天再為大家提供乙個實現單例模式的方法,註冊式單例模式。首先建立乙個容器類,用來盛放建立的單例。public class containersingleton2 private static map ioc newconcurrenthashmap public static object geti...

單例模式學習

通常情況下我們可以讓乙個全域性變數使得乙個物件被訪問,但不能防止你例項化多個物件,乙個最好的方法就是,讓類自身負責儲存他的唯一例項。這個類可以保證沒有其他例項可以被建立,並且他可以提供乙個訪問該例項的方法。一 經典單例 public class singleton public static sin...

學習單例模式

1 單例模式是怎樣產生的?當想要讓乙個定義類只能例項化乙個物件,則不能對外提供public的構造方法,而是要把構造方法定義為private的,這樣就可以讓定義類自己控制類的例項化。同時,也要對外提供乙個public的方法,用來得到定義類的例項。當然,在定義類的內部需要初始化來建立自身的乙個例項 保證...