Java併發編程(二):volatile關鍵字

volatile是Java虛擬機提供的輕量級的同步機制。volatile關鍵字有如下兩個作用,一句話概括就是內存可見性和禁止重排序。
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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章