Java併發學習之Volatile及內存模型探究

volatile工做原理

java編程語言容許線程訪問共享變量,爲了確保共享變量能被準確和一致的更新,線程應該確保經過排他鎖單獨得到這個變量。html

Java語言提供了volatile,在某些狀況下比鎖更加方便。若是一個字段被聲明成volatile,java線程內存模型確保全部線程看到這個變量的值是一致的。java

若想清楚理解volatile關鍵字是如何保障共享變量在多線程之間正常使用的須要瞭解如下幾點編程

  • java的內存模型
  • 原子性,可見性,有序性
  • volatile的工做原理
  • 測試case

I. Java的內存模型

1. 內存模型

精簡一點,概念以下:緩存

指令在CPU中執行,CPU運行速度較快,所以爲減小從內存中頻繁讀寫數據的開銷,在cpu與內存的操做之間,有個高速緩存的的區域多線程

獲取數據流程:併發

  • 從緩存中獲取Data
  • 緩存中存在,則直接返回
  • 緩存中不存在
    • 從內存中獲取Data數據
    • 將Data數據寫入緩存
    • 返回Data數據

上面的流程中,第一步會致使一致性問題,分析以下編程語言

若內存中Data已更新,但緩存中數據未更新,此時返回緩存中Data,返回的是舊數據測試

解決方案:ui

  • 總線上加LOCK#鎖
    • 由於CPU和其餘部件進行通訊都是經過總線來進行的,若是對總線加LOCK#鎖的話,也就是說阻塞了其餘CPU對其餘部件訪問(如內存),從而使得只能有一個CPU能使用這個變量的內存
  • 緩存一致性協議
    • 在內存中數據發生變動後,同時使全部緩存中的數據失效,在訪問緩存中數據時,優先判斷是否已經失效,若失效則從內存中獲取最新的數據回寫到緩存中,並返回

volatile

2. Java內存模型

java內存模型,主要是爲了屏蔽不一樣的硬件,操做系統的內存訪問差別,使Java程序能夠達到跨平臺的目的,從而定義的一套模型操作系統

線程之間的共享變量存儲在主內存中,每一個線程都有一個私有的本地內存,本地內存保存該線程讀寫共享變量的副本

所以也存在上面的一致性問題,即如何保證線程對共享變量的修改後,其餘的線程能訪問到最新的共享變量

指令重排序

Java內存模型中,容許編譯器和處理器對指令進行重排序,可是重排序過程不會影響到單線程程序的執行,卻會影響到多線程併發執行的正確性

舉例說明

int i;
boolean ans;

i = 10;
ans = true;

上面的代碼中,ians的賦值前後順序因爲指令重排,可能會出現ans=true時,i依然爲0的狀況

II. 原子性,可見性,順序性

原子性

表示不可再繼續分割

  • java中除了 (long,double)的賦值是非原子性的,其餘的基本變量、對象的賦值都是原子性的
  • ++/-- 操做是非原子性的

可見性

一個線程對共享變量的修改,確保對其餘線程可見(即另外一個線程能訪問到修改後的數據)

  • volatile進行聲明變量,保證可見

順序行

程序執行的順序按照代碼的前後順序執行

  • volatile 禁止指令重排
  • 在修改變量時,加鎖(synchronized,lock),確保同一時刻只有一個線程修改變量

III. Volatile關鍵字

用法

  • 在變量前面加上volatile便可

做用

  • 確保共享變量的修改,對其餘線程都是當即可見的
  • 禁止指令重排(即當訪問or修改volatile修飾的共享變量時,確保前面的代碼都執行完了)

原理和實現機制

  • 修改volatile聲明的共享變量,會強制要求修改後的值寫入內存,並失效其餘線程的本地內存中的副本
  • 彙編以後,加入volatile關鍵字時,會多出一個lock前綴指令
  • 它確保指令重排序時不會把其後面的指令排到lock指令以前,也不會把前面的指令排到lock指令以後

IV. 使用場景&小結

1.volatile關鍵字沒法保證操做的原子性

2.volatile關鍵字,禁止共享變量的指令重排,確保修改對全部線程當即可見

3.使用場景

  • 對變量的寫操做不依賴於當前值
    • (由於volatile不保證原子性,若依賴本身的值)
  • 變量獨立使用
    • 如 volatile 定義變量i,還有一個沒有被volatile修飾的變量j
    • int ans = i + j;(也沒法保障準確性)

經典的單例case寫法

class Singleton{
    private volatile static Singleton instance = null;
     
    private Singleton() {
    }
     
    public static Singleton getInstance() {
        if(instance==null) {
            synchronized (Singleton.class) {
                if(instance==null)
                    instance = new Singleton();
            }
        }
        return instance;
    }
}

參考

掃描關注,java分享

https://static.oschina.net/uploads/img/201710/13203703_6IVg.jpg

相關文章
相關標籤/搜索