public class Test { private static int init_value = 0; public static void main(String[] args) { new Thread(() -> { int a = init_value; while (a < 500) { if (a != init_value) { System.out.printf("the value is update to [%d]\n" , init_value); a = init_value; } } }, "read").start(); new Thread(() -> { int a = init_value; while (a < 500) { System.out.printf("the value is change to [%d]\n", a++); init_value = a; } }, "update").start(); } }
執行結果:線程update會一直執行,線程read不會執行,執行狀況一直如此。java
緣由分析:線程read,沒法感知init_value的變化,由於線程read中讀到的init_value值一直都是緩存中的值,沒有讀到主內存的值,因此init_value的值永遠是0.緩存
緩存的效率是比主存的速度快的,加入緩存的目的是爲了提升訪問速度,可是加入緩存後也出現了數據一致性的問題,尤爲是在多線程的環境下。多線程
程序處理a++的具體流程:
一、讀取主存中的 a 到CPU Cache中。線程
二、對a進行加1操做。code
三、將結果寫入到CPU Cache中。blog
四、將數據刷新到主存中。排序
在加上valotile以後在read線程中就能看到init_value的變化。內存
private volatile static int init_value = 0;資源
可見volatile的做用之一就是保證變量的可見性,它能讓數據直接同步到主存中去,讓緩存中的數據失效。同步
當一個變量被volatile關鍵詞修飾時,對於共享資源的讀操做會直接在主存中執行,固然也會緩存到工做內存,當其餘線程對共享資源進行修改,會致使當前線程在工做內存中的共享資源失效,因此必須從主存中再次獲取,對於共享資源的寫操做固然是要先修改工做內存,修改結束後再刷新到主內存。所以volatile關鍵字沒法保證原子性,只能保證可見性。
volatile關鍵詞直接禁止JVM對volatile關鍵字修飾的指令進行從新排序。但對於先後無依賴關係的指令則能夠隨意排序。
int x = 0; int y = 1; volatile int z = 20; x++; y--;
volatile int z = 20;以前,x和y的執行順序並不關心,只要能保證到執行z = 20時,x=0 y=1就好了。
至於後面的x++ y--哪條指令先執行也不用關心。
private volatile boolean initialized = false; private Context context; public Context load() { if (initialized) { context = loadContext(); initialized = true; // 禁止指令重排 } return context; }
initialized被volatile修飾,這就意味着,當initialized=true時,loadContext()方法是必定執行完成的。