理解volatile

1.volatile簡介java

     

/**
 * @author Lin
 * @Date 2018/2/22.
 */
public class TestVolatile {

    private static boolean isOver = false;

    public static void main(String[] args) {
        new Thread( () -> {while (!isOver){
            System.out.println("111");
        }} ).start();
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        isOver = true;
        System.out.println("End");
    }
}

先看一段代碼,啓動一個線程,因爲isOver = false,因此該線程中run方法的while是死循環。企圖在main方法中更改isOver的值去終止線程,可是實時上該程序的線程並無被終止,始終陷入死循環中,線程並無終止退出。緩存

首先咱們對volatile的基本認知是「被volatile修飾的變量對每一個線程是可見的,即一個線程修改了某個變量的值,這新值對其餘線程來講是當即可見的,從而避免數據髒讀」。spa

2.深刻理解volatile關鍵字線程

  1. volatile保證可見性

    當變量被volatile修飾時會具有兩種語意:
      1)保證了不一樣線程對這個變量進行操做時的可見性,即一個線程修改了某個變量的值,這新值對其餘線程來講是當即可見的。
            解釋下上面代碼中線程不能被終止,熟悉java內存相關知識的都知道每一個線程在運行時都有本身的工做內存,那麼上面線程運行是會將isOver變量拷貝一份到本身的工做內存中,當main線程去修改isOver的          值時,並不會影響到自定義線程工做內存中的isOver的值。
            可是使用volatile修飾就不同了:
           1)使用volatile關鍵字會強制將修改的值當即寫入主存
           2)使用volatile以後當main線程修改isOver後會致使自定義線程工做內存中的isOver無效(反映到硬件層的話,就是CPU的L1或者L2緩存中對應的緩存行無效),因此自定義線程再次讀取變量isOver的值時會            去主存讀取。
      2)禁止進行指令重排序

  2. volatile不確保原子性(此處要注意,很少記錄

3.volatile的實現原理code

    可見性:blog

         在生成彙編代碼時會在volatile修飾的共享變量進行寫操做的時候會多出Lock前綴的指令。咱們想這個Lock指令確定有神奇的地方,那麼Lock前綴的指令在多核處理器下會發現什麼事情了?主要有這兩個方面的影           響:排序

  1. 將當前處理器緩存行的數據寫回系統內存;
  2. 這個寫回內存的操做會使得其餘CPU裏緩存了該內存地址的數據無效

        爲了提升處理速度,處理器不直接和內存進行通訊,而是先將系統內存的數據讀到內部緩存(L1,L2或其餘)後再進行操做,但操做完不知道什麼時候會寫到內存。若是對聲明瞭volatile的變量進行寫操做,JVM就會向          處理器發送一條Lock前綴的指令,將這個變量所在緩存行的數據寫回到系統內存。可是,就算寫回到內存,若是其餘處理器緩存的值仍是舊的,再執行計算操做就會有問題。因此,在多處理器下,爲了保證各個處          理器的緩存是一致的,就會實現緩存一致性協議,每一個處理器經過嗅探在總線上傳播的數據來檢查本身緩存的值是否是過時了,當處理器發現本身緩存行對應的內存地址被修改,就會將當前處理器的緩存行設置成           無效狀態,當處理器對這個數據進行修改操做的時候,會從新從系統內存中把數據讀處處理器緩存裏。所以,通過分析咱們能夠得出以下結論:內存

  1. Lock前綴的指令會引發處理器緩存寫回內存;
  2. 一個處理器的緩存回寫到內存會致使其餘處理器的緩存失效;
  3. 當處理器發現本地緩存失效後,就會從內存中重讀該變量數據,便可以獲取當前最新值。

   有序性:io

       Lock前綴指令實際上至關於一個內存屏障(也成內存柵欄),它確保指令重排序時不會把其後面的指令排到內存屏障以前的位置,也不會把前面的指令排到內存屏障的後面;即在執行到內存屏障這句指令時,在它          前面的操做已經所有完成。class

 

理解了volatile就處理上面代碼的問題了。

/**
 * @author Lin
 * @Date 2018/2/22.
 */
public class TestVolatile {

    private static volatile boolean isOver = false;

    public static void main(String[] args) {
        new Thread( () -> {while (!isOver){
            System.out.println("111");
        }} ).start();
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        isOver = true;
        System.out.println("End");
    }
}
相關文章
相關標籤/搜索