Java併發編程之volatile

上一篇文章中咱們介紹了 Synchronzied ,接下來咱們來介紹 volatile。安全

咱們知道 Java 內存模型有三大特性,有序性、可見性、原子性。bash

咱們首先來談一談有序性。

那麼什麼是有序性呢?Talk is cheap,show me the code 咱們直接來看代碼。多線程

public static void main(String[] args) {
             int num1=10;//代碼1
             int num2=5;//代碼2
             num1=num1-num2;//代碼3
             num2=num1*num1;//代碼4
       }
複製代碼

這段代碼的執行順序必定是代碼一、二、三、4 嗎?這是不必定的,爲何呢?由於可能會發生指令重排序。指令重排序就是在不影響程序最終結果的前提下,爲了提高程序的執行效率,可能會對輸入的代碼順序進行優化,形成執行順序不必定按照代碼書寫順序的一種形式。優化

咱們來看看指令重排序的定義,一個必要的條件就是不影響程序的執行結果,那麼想一想看咱們的代碼3和代碼4的執行順序可否調換順序呢?答案天然是否認的,由於代碼4須要依賴代碼3的內容。spa

在單線程的狀況下,重排序不會影響程序的運行結果,那麼在多線程狀況下呢?咱們來看一看。線程

//線程1:
context = loadContext();   //語句1
inited = true;             //語句2
//線程2:
while(!inited ){
  sleep()
}
doSomethingwithconfig(context);
複製代碼

首先在線程1中先執行語句2,而後切換到線程2來執行。這個時候線程二認爲初始化已經完成,就會進行接下來的操做,可是 context 並無初始化,因此就會致使代碼出錯。code

接下來是可見性

可見性是指當一個線程修改了共享變量後,其餘線程可以當即得知這個修改。排序

咱們使用 volatile 關鍵字能夠保證可見性,使得各個線程均可以讀取到最新的值內存

最後是原子性

原子性是指一個操做是不可中斷的,要麼所有執行成功要麼所有執行失敗,有着「同生共死」的感受。及時在多個線程一塊兒執行的時候,一個操做一旦開始,就不會被其餘線程所幹擾。it

咱們來看一個最簡單的例子,代碼 i++ 是原子性操做嗎?其實 i++ 能夠分爲3步來看待。

  • 讀取 i 值
  • 對 i +1
  • 把新值賦給 i

這其中任何一步發生均可能發生線程安全問題,因此 i ++ 不是原子性操做。

介紹完咱們的三大特性,咱們回過頭再來講  volatile , volatile 關鍵字能夠作到可見性和有序性。也就是能夠保證變量獲取最新的值以及禁止指令重排序,可是它不能實現原子性,因此 volatile 的使用具備侷限性,咱們簡單地來總結一下它的使用場景。

  • 對變量的寫入操做不依賴當前的值,或者你能肯定只有單個線程可以更新變量的值
  • 該變量不會和其餘狀態變量一塊兒歸入不變性條件中
  • 在訪問變量時不須要加鎖
相關文章
相關標籤/搜索