1)保證被volatile修飾的共享變量對所有線程總是可見的,也就是當一個線程修改了一個被volatile修飾共享變量的值,新值總是可以被其他線程立即得知。
2)禁止指令重排序優化。在執行程序時爲了提高性能,編譯器和處理器通常會重新安排指令的執行順序。
public class T { /*volatile*/ boolean running=true; void m(){ System.out.println("m start"); while (running){} System.out.println("m end"); } public static void main(String[] args) { T t=new T(); new Thread(()->t.m(),"t1").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } t.running=false; } }
這段代碼中沒加volatile的話,循環的後面的輸出語句"m end"不會輸出,但是main方法裏面確實是已經將running的值改成了false。這裏有關java的內存模型,簡單來說就是程序運行時,running的值已經被加載到了緩存裏面,而改的是t對象的running值在堆內存中。加了volatile就不一樣了,它改完數值之後,會通知其他線程,讓其他線程重新讀一下堆內存中的值,也就是內存可見性。
public class T1 { volatile int count=0; /*synchronized*/ void m(){ for (int i=0;i<10000;i++){ count++; } } public static void main(String[] args) { T1 t=new T1(); List<Thread> threads=new ArrayList<Thread>(); for(int i=0;i<10;i++){ threads.add(new Thread(()->t.m(),"thread-"+i)); } threads.forEach((o)->o.start()); threads.forEach((o)->{ try { o.join(); } catch (InterruptedException e) { e.printStackTrace(); } }); System.out.println(t.count); } }
這段代碼中,十個線程執行count++加到1w循環,理想狀態當然是加到10w,但是volatile只能保持線程的可見性,不能保持原子性。給count變量加上了volatile,最後輸出的count值也達不到10w。這就是volatile與synchronized的區別,volatile只能保持可見性,而synchronized可以保持可見性和原子性。但是volatile更加輕量級,能用volatile的情況下,儘量別用synchronized。