java併發--用實例從內存角度分析volatile

引入
詳細瞭解相關內容推薦對比閱讀。
JVM內存模型:
https://blog.csdn.net/FortressBesieged/article/details/88066898
volatile:
https://www.cnblogs.com/dolphin0520/p/3920373.html
synchronized
https://www.cnblogs.com/lcplcpjava/p/6858135.html
lock
https://www.cnblogs.com/dolphin0520/p/3923167.html
https://www.cnblogs.com/shen-smile/p/5142292.html

volatile

場景:

有一個使用volatile修飾的變量 i = 10,此時有兩個線程A,B共同對變量1進行“i++”操作。

public class Test {
    public volatile int inc = 0;
    public void increase() {
        inc++;
    }
    public static void main(String[] args) {
        final Test test = new Test();
        for(int i=0;i<10;i++){
            new Thread(){
                public void run() {
                    for(int j=0;j<1000;j++)
                        test.increase();
                };
            }.start();
        }
        while(Thread.activeCount()>1)  //保證前面的線程都執行完
            Thread.yield();
        System.out.println(test.inc);
    }
}

注:i++對於線程來說並不是原子操作,包括三個步驟:從主內存中將變量i拷貝到自己的緩存中,在寄存器中執行自增操作,將操作後的變量寫入自己的緩存。
分析:
某時刻變量i=10位於主內存中,線程A將變量i拷貝到了自己的緩存中,此時還未執行自增操作,線程阻塞。
此時線程B將變量i=10拷貝到自己的緩存(此時線程A未對變量i完成修改,並未將修改後的變量寫入主內存),此時的i=10,線程B執行自增操作,寫入緩存後同步到了主內存,此時主內存的i值爲11。
若此時線程A繼續執行,由於“volatile保證了新值能夠立即同步到主內存中,以及每次使用前立即從主存中刷新”,對於此時的線程執行i++操作過程來說,每次指的是三個步驟全部完成,纔算一次完成。所以,對於線程A來說,繼續執行自增操作時,此時的變量i仍然是10並不是線程B修改後的11。這樣,兩個線程對於一個變量都執行自增操作,變量只增加了一次,所以volatile並不能保證原子性。所以說,若想正確使用volatile,那麼對volatile修飾的內容執行的操作一定是原子操作纔可以,對比上述場景來說,就是線程A在獲取到變量i時,在寫回到主內存之前都不會被阻塞,這樣當線程B獲取值時就不會獲取到未被線程A操作過程中的值,這也就是synchronized關鍵字實現的內容。

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