變量可見性問題

簡介

併發編程時各個線程中無法獲取到共享變量的最新值。


產生的原因

共享變量存儲在主內存中,通常情況下各個線程在使用某個共享變量時先將共享變量複製進線程工作內存中,後續使用到該變量時直接從當前線程工作內存中獲取變量值,此時如果其他線程更改了該共享變量值那麼當前線程無法實時更新到該變量的最新值。
同步交互協議中規定的8種原子操作

  • lock:將主內存中的變量鎖定,爲某一個線程獨佔
  • unlock:將lock加的鎖解除,此時該變量可以被其他線程訪問
  • read:作用於主內存變量,將主內存中的變量讀到工作內存當中
  • load:作用於工作內存變量,將read讀取到的值保存到工作內存的變量副本中
  • use:作用於工作內存變量,將值傳遞給線程代碼執行引擎
  • assign:作用於工作內存變量,將執行引擎返回的值重新賦值給變量副本中
  • store:作用於工作內存變量,將變量副本的值傳遞給主內存
  • write:作用於主內存變量,將store傳過來的值寫入到主內存共享變量中

示例

如下代碼while循環會一直執行知道cpu達到100%應用程序崩潰
p

rivate static boolean is=true;
public static void main(String[] args){
    new Thread(new Runnable() {
        @Override
        public void run() {
            int i=0;
            while(VisibilityDemo.is){
                i++;
            }
            System.out.println("循環終止");
            System.out.println(i);
        }
    }).start();
    try{
        TimeUnit.SECONDS.sleep(2);
    }catch (InterruptedException ex){
        ex.printStackTrace();
    }
    VisibilityDemo.is=false;
    System.out.println("被置爲false了");
}

解決方式

final

final修飾的變量不可變,所以不存在變量可見性問題


synchronized

synchronized語義規範

  1. 進入同步塊錢,先清空工作內存中的共享變量,從主內存中重新加載
  2. 解鎖前,必須把共享變量同步回主內存
    示例
private static boolean is=true;
    public static void main(String[] args){
        new Thread(new Runnable() {
            @Override
            public void run() {
                int i=0;

                while(VisibilityDemo.is){
                    synchronized (this){
                        i++;
                    }
                }
                System.out.println("循環終止");
                System.out.println("i="+i);
            }
        }).start();
        try{
            TimeUnit.SECONDS.sleep(2);
        }catch (InterruptedException ex){
            ex.printStackTrace();
        }
        VisibilityDemo.is=false;
        System.out.println("被置爲false了");
    }

運行結果

被置爲false了
循環終止
i=64274144

volatile

volatile語義規範

  1. 使用volatile變量時,必須重新從主內存加載,並且read、load是連續的
  2. 修改volatile變量後,必須立馬同步回主內存,並且store、write是連續的

示例

private static volatile boolean is=true;
public static void main(String[] args){
    new Thread(new Runnable() {
        @Override
        public void run() {
            int i=0;
            while(VisibilityDemo.is){
                i++;
            }
            System.out.println("循環終止");
            System.out.println(i);
        }
    }).start();
    try{
        TimeUnit.SECONDS.sleep(2);
    }catch (InterruptedException ex){
        ex.printStackTrace();
    }
    VisibilityDemo.is=false;
    System.out.println("被置爲false了");
}

執行結果

被置爲false了
循環終止
-860124361

synchronized和volatile區別

synchronized使用鎖機制保護共享資源,只有獲得鎖的線程纔可操作共享資源,同時synchronized語義規範保證了修改共享資源後會同步回主內存,從而做到線程安全。volatile因爲沒有鎖機制,所以線程還是可以併發操作變量,所以沒有做到線程安全
volatile僅能使用在變量級別;synchronized則可以使用在變量、方法、和類級別的
volatile不會造成線程的阻塞;synchronized可能會造成線程的阻塞
volatile標記的變量不會被編譯器優化;synchronized標記的變量可以被編譯器優化

發佈了36 篇原創文章 · 獲贊 28 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章