Volatile的作用


眾所周知,volatile關鍵字可以讓線程的修改立刻通知其他的線程,從而達到數據一致的作用。那么它具體涉及到哪些內容呢?

關於緩存

計算機最大的存儲空間就是磁盤(硬盤),但是訪問的速度也是最慢的,價格最便宜;再就是內存,容量更小,造價更高,但是速度也更快。不過跟cpu的計算速度比起來,那就太慢了。可以想像,如果cpu每次計算都要從內存讀取數據,那大部分的時間估計都浪費在這上面了。所以就引入了緩存的概:

緩存的結構大概時這樣的,從1級到3級速度越來越慢,最后通過總線與內存連接。如果時多核多cpu,那么結構大概是這樣的:

多線程造成的緩存不一致

由於現在大部分的機器都有多個cpu,這就導致如果時運行多線程的任務,就可能運行在不同的cpu上。試想一下:

int a = 0;
int b = a;
b += 1;

如果開啟兩個線程執行,我們想要的結果是3,但是最后的結果只是2。這是因為在做加法運算的時候,cpu會先把a的值讀入cpu的緩存,然后更新緩存,在更新內存。很有可能兩個線程分散在兩個cpu,每個都是對自己緩存內的數據進行讀寫,這樣就造成了結果不一致的現象。

volatile的作用

volatile的作用就是當一個線程更新某個volatile聲明的變量時,會通知其他的cpu使緩存失效,從而其他cpu想要做更新操作時,需要從內存重新讀取數據。具體的通知方式,一種是通過某種協議,比如MESI;再就是對總線加鎖,控制變量的讀取。具體硬件上怎么個流程,我就搞不清楚了...

並發

這里還需要強調的時,並發編程涉及的三個特性:原子性、可見性、有序性。就好像分布式里面的cap一樣,需要熟知。先來通俗的描述下:

原子性

即要么全做,要么全部做。比如從a銀行轉錢到b銀行。

在編程中,除了long或者double外的變量更新就是原子操作。long和double除外,是因為它們在32位的操作系統上,會被分成兩部分進行更新,此時就不是原子的。

再比如最常見的i++也不是原子的,它相當於先讀取i進行+1操作更新三個步驟進行。

可見性

多個線程訪問同一個變量時,這個變量被修改后,能被其他的線程看到。

有序性

比如

int a = 10; 
int r = 2;   
a = a + 3;  
r = a*a;    

這段代碼有可能進行指令的重排,從而導致結果跟預期的不一致。指令的重排需要按照happens-before原則,比如:

  1. 程序次序原則,一個線程內,按照書寫的順序執行
  2. 鎖定原則,lock前后執行
  3. volatile原則,volatile變量前后執行
  4. 傳遞原則,如果a需要調用b,那么a就會在b的前面

...

等等...

volatile的特性

volatile只能保證變量的可見性、有序性,但是不能保證原子性。因此可以用它來做double-check,但是不能來做i++的操作。如果想要實現i++的可靠性,必須依賴於synchronized、lock或者atomicXXX來實現。

參考

  1. 海子的《Java並發編程:volatile關鍵字解析》:http://www.cnblogs.com/dolphin0520/p/3920373.html
  2. liuxiaopeng的《Java 並發編程:volatile的使用及其原理》:https://www.cnblogs.com/paddix/p/5428507.html
  3. double check http://blog.csdn.net/dl88250/article/details/5439024
  4. cpu緩存知識:http://blog.jobbole.com/36263/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM