Java 多線程:volatile 多線程同步關鍵字

volatile 也是 多線程的解決方案之一。volatile 能夠保證 可見性,但是不能保證原子性。它只能作用於變量,不能作用於方法。當一個變量被聲明爲 volatile 的時候,任何 對該變量的讀寫都會繞過 高速緩存,直接讀取主內存的變量的值。

如何理解直接讀寫主內存的值:回到 多線程產生的原因 ,在 i++ 操作的時候,當 進行 執行引擎 對 變量  進行 + 1 之後,原來 是應該寫入到 本地內存中,再由本地內存寫入到主內存中,但是 由於 變量使用了 volatile 的修飾,所以 該值不會經過本地內存,而是直接寫入到 主內存中去。 讀取也是同樣的道理。

使用volatile 有兩點需要注意的地方:

  • 運算結果並不依賴於當前值,或者能確保只有單一的線程能夠修改變量的值。
  • 變量不需要和其他的狀態變量共同參與不變約束
對於第一點的理解:
public class main {  
    public static int i = 0;  
    public static void main(String args[]){  
          
        new Thread(new Runnable(){  
            public void run(){  
                for(int j = 0; j < 10000; j++)  
                    i++;  
                System.out.println("Thread1 end...");  
            }  
        }).start();;  
          
        new Thread(new Runnable(){  
            public void run(){  
                for(int j = 0; j < 10000; j++)  
                    i++;  
                System.out.println("Thread2 end...");  
            }  
        }).start();  
          
        i++;  
          
        try {  
            Thread.sleep(500);  
        } catch (InterruptedException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
         ,
        System.out.println("i = " + i);  
    }  
}  
以上程序不一定全會打印出 20000.分析如下:
  • 正常情況下:A 線程取得 主內存的值 i,進行修改,再寫回到 主內存,B線程 取得主內存的值 i,進行修改,再寫回主內存。這種情況下是正常打印出 20000。
  • 非正常情況下:A線程取得 主內存的值 i, B 內存取得主內存的值 i,A線程修改後寫回主內存,B線程修改後寫回主內存。這種情況下,結果就少了 1.
這也是爲什麼說 volatile 只是保證可見性,不保證原子性。所以說,只能有單一的線程修改共享變量,纔可以使用 volatile。


對於第二點的理解:

private Date start;    
private Date end;    
    
public void setInterval(Date newStart, Date newEnd) {    
    // 檢查start<end是否成立, 在給start賦值之前不變式是有效的    
    start = newStart;    
    
    // 但是如果另外的線程在給start賦值之後給end賦值之前時檢查start<end, 該不變式是無效的    
    
    end = newEnd;    
    // 給end賦值之後start<end不變式重新變爲有效    
}  

最後,關於什麼時候使用 volatile,一般是用來當做標記來使用。比如說,當shutdown() 方法被調用的時候,所有的 doWork() 方法都會停下來。




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