synchronized 是乙個重量級的鎖,volatile
通常被比喻成輕量級的synchronized
volatile
是乙個變數修飾符,只能用來修飾變數。
volatile寫:當寫乙個volatile變數時,jmm會把該執行緒對應的本地記憶體中的共享變數重新整理到主記憶體。
volatile讀:當讀乙個volatile變數時,jmm會把該執行緒對應的本地記憶體置為無效。執行緒接下來將從主記憶體中讀取共享變數。
1)jmm把記憶體屏障指令分為下列四類:
storeload barriers是乙個「全能型」的屏障,它同時具有其他三個屏障的效果。現代的多處理器大都支援該屏障(其他型別的屏障不一定被所有處理器支援)。執行該屏障開銷會很昂貴,因為當預處理器通常要把寫緩衝區中的資料全部重新整理到記憶體中(buffer fully flush)。
store:資料對其他處理器可見(即:重新整理到記憶體)
load:讓快取中的資料失效,重新從主記憶體載入資料
2)jmm針對編譯器制定的volatile重排序規則表
是否能重排序
第二個操作
第乙個操作
普通讀/寫
volatile讀
volatile寫
普通讀/寫
novolatile讀
nono
novolatile寫
nono
舉例來說,第三行最後乙個單元格的意思是:在程式順序中,當第乙個操作為普通變數的讀或寫時,如果第二個操作為volatile寫,則編譯器不能重排序這兩個操作。
從上表我們可以看出:
jmm記憶體屏障插入策略(編譯器可以根據具體情況省略不必要的屏障):
在每個volatile寫操作的後面插入乙個storeload屏障。
在每個volatile讀操作的後面插入乙個loadload屏障。
在每個volatile讀操作的後面插入乙個loadstore屏障。
volatile保證可見性
volatile修飾的變數寫之後將本地記憶體重新整理到主記憶體,保證了可見性
volatile保證有序性
volatile變數讀寫前後插入記憶體屏障以避免重排序,保證了有序性
volatile不保證原子性
volatile不是鎖,與原子性無關
要我說,由於cpu按照時間片來進行執行緒排程的,只要是包含多個步驟的操作的執行,天然就是無法保證原子性的。因為這種執行緒執行,又不像資料庫一樣可以回滾。如果乙個執行緒要執行的步驟有5步,執行完3步就失去了cpu了,失去後就可能再也不會被排程,這怎麼可能保證原子性呢。
為什麼synchronized
可以保證原子性 ,因為被synchronized
修飾的**片段,在進入之前加了鎖,只要他沒執行完,其他執行緒是無法獲得鎖執行這段**片段的,就可以保證他內部的**可以全部被執行。進而保證原子性。(摘自
volatile不保證原子性的例子:
/*** 建立10個執行緒,然後分別執行1000次i++操作。目的是程式輸出結果10000
* 但是,多次執行的結果都小於10000。這其實就是volatile無法滿足原子性的原因。
*/public class test
public static void main(string args) ;
}.start();
}while (thread.activecount() > 1)
// 保證前面的執行緒都執行完
thread.yield();
system.out.println(test.inc);}}
java併發 volatile關鍵字
從記憶體語義的角度來說,volatile的寫 讀與鎖的釋放 獲取有相同的記憶體效果 volatile寫和鎖的釋放有相同的記憶體語義 volatile讀與鎖的獲取有相同的記憶體語義。當寫乙個volatile變數時,jmm會把該執行緒對應的本地記憶體中的共享變數值重新整理到主記憶體。jmm針對編譯器制定...
Java併發程式設計 JMM和volatile關鍵字
通過快取一致性協議 string s abc int i 0 i i 0 主記憶體 i 執行緒1 j i 執行緒22.被volatile關鍵字修飾變數不會指令重排序。public class novisibility private static class readthread extends t...
Java併發程式設計之volatile變數
volatile提供了弱同步機制,用來確保將變數更新通知到其它執行緒。volatile變數不會被快取在暫存器中或者對其它處理器不可見的地方,因此在讀取volatile變數時總會返回最新寫入的值。可以想象成如下語義,然而volatile是更輕量級的同步機制。volatile只能確保可見性,但不能保證原...