java中volatile詳解
1.1 作用:它用來確保將變數的更新操作通知到其他執行緒。 volatile可以保證執行緒可見性且提供了一定的有序性,但是無法保證原子性。 1.保證可見性、不保證原子性 2.禁止指令重排序 可見性的實現: (1)修改volatile變數時會強制將修改後的值重新整理的主記憶體中。 (2)修改volatile變數後會導致其他執行緒工作記憶體中對應的變數值失效。因此,再讀取該變數值的時候就需要重新從讀取主記憶體中的值。
從彙編角度起步理解實現原理: volatile變數修飾的共享變數進行寫操作的程式碼轉變為彙編程式碼,會有Lock字首的指令。 Lock字首指令在多核處理器中會引發兩件事: (1)(volatile寫的記憶體語義)將當前執行緒的工作記憶體中關於這個共享變數的資料寫回主記憶體; (2)(volatile讀的記憶體語義)這個寫回主記憶體的操作會使其他執行緒的工作記憶體快取這個共享變數的資料無效(過期了); 詳細說明: 為了提高處理速度,執行緒一般會直接先從主記憶體中複製一份到自己的工作記憶體中,然後在工作記憶體中進行操作,然後會選擇一個時間,將工作記憶體中的資料寫回主記憶體中(這個時間不確定)。如果聲明瞭volatile的變數進行寫操作,JVM就會向處理器傳送一條Lock字首的指令,會將這個變數快取線上程工作記憶體中的資料寫回主記憶體中。剩下要處理的就是如何將其他執行緒快取該變數的值進行更新?在多處理器的情況下,為了保證各個處理器的快取是一致的,就會實現快取一致性協議,每個處理器通過嗅探在總線上傳播資料來檢查自己快取的值是不是過期了,當處理器發現自己快取行對應的記憶體地址被修改了,就會將當前處理器的快取行設定成無狀態,當處理器對這個資料進行修改操作的時候,會重新從系統記憶體中把資料讀取到處理器快取裡。
有序性的實現: volatile限制作業系統進行指令重排序(編譯器重排序和處理器重排序)。而實現volatile可見性和happen-befor的語義是由JVM通過一組叫做記憶體屏障的處理器指令完成的,它可以實現對記憶體順序的限制。 對於原子性: volatile只能保證讀/寫操作單個的原子性,如果一個操作包含了讀和寫之類的複合操作,volatile不能保證其原子性。
1.2 Volatile變數是比鎖更加輕量級的一種同步機制。 執行緒在訪問volatile變數的時候不會執行加鎖操作,所以也不會執行執行緒阻塞,因此volatile是一種比synchronized更加輕量級的同步機制。
1.3 volatile變數對可見性的影響比volatile變數本身更加重要。
1.4 使用volatile變數的條件 (1)對變數的寫入操作不依賴變數的當前值,或者你能確保只有單個執行緒更新變數的值; (2)該變數不會與其他狀態變數一起納入不變性條件中; (3)在訪問變數時不需要鎖。 比如在第一個條件中,遇到要保證遞增操作(i++)的原子性時候,就不能只靠使用volatile變數,因為volatile只能確保可見性,不能確保原子性。 對於第二個條件,如果程式中要求儲存當前的值和上一個值,如果都用volatile變數修飾,就有兩個volatile變數。但是在儲存當前的值的時候,不能原子性地儲存上一個值,這樣就會造成在儲存這兩個值的間隙可能會被其他執行緒修改值,產生程式不正常執行。