1. 程式人生 > >Java併發程式設計 之 volatile

Java併發程式設計 之 volatile

國慶已經結束了,本該在國慶前就應該做好這篇筆記的,怎奈自己太懶,就在今天把前幾天的知識梳理一下。在前幾篇的部落格介紹了一點併發程式設計的相關知識,今天我對volatile的原理簡單的闡述一下。

要想理解volatile的原理,需要對JMM(Java記憶體模式)有些瞭解。

在實際的程式執行中,由於cpu執行速度很快,而從記憶體讀取資料和向記憶體寫入資料的過程跟cpu執行指令的速度比起來要慢的多,於是在cpu就引入了快取。

也就是說在執行的時候主存中的資料會在快取有一份拷貝,cpu的操作是針對這個拷貝去進行的,最後在把結果寫會到主存中。


這裡寫圖片描述

可是這樣在多執行緒執行就有可能出現問題。比如:i=i+1;(i的初始值為1)

假設有兩個A、B執行緒執行這個程式,一開始兩個執行緒都分別有i這個值的拷貝存在快取中。A先對快取進行i+1的操作,快取中i這時候已經是2了,然後寫會到主存中,主存也是2。

但是B執行緒在執行在主存被重新整理之前,就已經有了i的拷貝,也就是說執行緒B的快取i的值還是1,而不是A執行緒執行完畢的2,那麼這就出現了錯誤。

但是這裡就算是用volatile來修飾這個i,程式的結果還是有可能會報錯。要想用對volatile就要保證資料操作的原子性。

用volatile修飾一個變數的時候,當對該變數有寫的操作,會強制將修改的值立即寫入主存,它可以保證其他執行緒每次的讀取,都是一個最新的值。

這就是volatile的可見性,可見性只能保證每次讀取的是最新的值,但是volatile沒辦法保證對變數的操作的原子性。如i=i+1,它包括讀取變數的原始值、進行加1操作、寫入工作記憶體。就算你用volatile來修飾i也還是有可能會出錯。情況和上面是一樣的。

雖然A執行緒是把主存重新整理了,發出訊號,讓其他擁有該變數的執行緒中的資料變成無效狀態,當處理器對這個資料進行修改操作的時候,就會從記憶體中重新讀取。但是B執行緒是在主存被重新整理之前就讀取了,並且已經完成了對i+1的操作,後面的操作只是對i的寫回記憶體操作,並沒有涉及到修改操作,所以這樣還是保證不了資料的正確性。

所以要想用好volatile,就要保證操作的原子性。