android volatile的使用
今天,簡單講講android里的volatile的使用。
這個其實很簡單,而且我基本沒有用到,但是還是記錄一下。volatile的作用基本和sychronized相似,但是不能替代sychronized。
? volatile用處說明
? ? 在JDK1.2之前,Java的內存模型實現總是從主存(即共享內存)讀取變量,是不需要進行特別的注意的。而隨著JVM的成熟和優化,現在在多線程環境下volatile關鍵字的使用變得非常重要。
在當前的Java內存模型下,線程可以把變量保存在本地內存(比如機器的寄存器)中,而不是直接在主存中進行讀寫。這就可能造成一個線程在主存中修改了一個變量的值,而另外一個線程還繼續使用它在寄存器中的變量值的拷貝,造成數據的不一致。
這個基本就是volatile的使用,可以修飾變量,當多線程訪問時,直接訪問變量的內存而不是緩存,可以使用與多線程并發。但是存在問題,下面說一下:
volatile為什么不能保證原子性?
現在我們的手機都是多核的,也就是說同時有好幾顆CPU在工作,每顆CPU都有自己的Cache高速緩存,因為CPU的速度特別快,而內存的讀取操作相對于CPU的運算速度來說很慢,所以就會拖累CPU的效率,引入Cache就是為了解決這個問題的,CPU先把需要的數據從內存中讀到Cache中,然后直接和Cache來打交道,Cache的速度很快,因此可以保證CPU的工作效率,當Cache中的數據改變后,再將被改變的數據寫回內存中。
首先我們分析一下多線程在訪問一個普通的(沒有加volatile修飾符)的變量的過程
1.CPU1和CPU2先將count變量讀到Cache中,比如內存中count=1,那么現在兩個CPU中Cache中的count都等于1
2.CPU1和CPU2分別開啟一個線程來對count進行自增操作,每個線程都有一塊自己的獨立內存空間來存放count的副本。
3.線程1對副本count進行自增操作后,count=2 ; 線程2對副本count進行自增操作后,count=2
4.線程1和線程2將操作后的count寫回到Cache緩存的count中
5.CPU1和CPU2將Cache中的count寫回內存中的count。
那么問題就來了,線程1和線程2操作的count都是一個count副本,這兩個副本的值是一樣的,所以進行了兩次自增后,寫回內存的count=2。而正確的結果應該為count=3。這就是多線程并發所帶來的問題
如果變量count用volatile修飾了可以解決這個問題嗎?
如果一個變量加了volatile關鍵字,就會告訴編譯器和JVM的內存模型:這個變量是對所有線程共享的、可見的,每次JVM都會讀取最新寫入的值并使其最新值在所有CPU可見。我們再來看一下線程在訪問一個加了volatile修飾符的變量的過程
當count用volatile關鍵字修飾后,CPU1對count的值更新后,在寫回內存的同時會通知CPU2 count值已經更新了,你需要從內存中獲取count最新的值!
注意:這里說CPU1通知CPU2其實是不嚴謹的,其實這是緩存一致性機制在其作用,緩存一致性機制會阻止同時修改由兩個以上處理器緩存的內存區域數據,當其他處理器回寫已被鎖定的緩存行數據時,會使緩存行無效,當CPU1將新數據寫回內存后,會修改該數據在內存中的內存地址,CPU2通過嗅探在總線上傳播的數據來檢查自己的緩存行對應的內存地址是否被修改,如果被修改則將CPU2的該數據緩存行設置成無效狀態,當處理器對這個數據進行修改操作的時候,會重新從系統內存中把數據讀到CPU2的緩存行里。其實并不是CPU1通知CPU2,而是CPU2自己去嗅探。
其實大家只要明白了原理,怎么說也無所謂,就像好多地方都說volatile修飾的變量,線程直接和內存交互,不會保存副本。而實際上線程還是會保存副本,只不過CPU每次都會從內存中拿到最新的值,并且改變數據之后立馬寫回內存,看上去就像線程直接和內存交互一樣。
然后CPU2中的線程如果需要使用到count的時候,就會再從內存中讀取count的值來更新自己的Cache。這看上去似乎解決了我們的問題,其實問題依然存在,我們來分析一下:
比如當前count=1,CPU1和CPU2的Cache中的count都等于1,CPU1中的線程1對count進行了自增操作,然后CPU1更新了內存中count的值,并且通知CPU2 count的值已經改變,然后CPU2從內存中將count=2讀到了Cache中,并且線程2開始執行count的自增操作,而就在CPU2剛剛將count的值讀回Cache的時候,CPU1又更新了count的值,此時count=3,并且通知CPU2,但是此時線程2已經開始執行了,線程2已經將count=2拷貝到自己的內存空間中了,所以即使CPU2再次更新自己Cache中的count=3,也來不及了,線程2操作的是他自己內存空間中的count副本,所以線程2給count做完自增操作后,將count=3并且寫回Cache,CPU2更新內存中的count。此時count的值應該是4,然而CPU2更新完count的值后仍然等于3,這樣就出現了錯誤。我們考慮的是只有兩顆CPU的情況,但是現在市面上已經有8核手機了!如果8顆CPU同時工作的話,錯誤會更嚴重!
Volatile一般情況下不能代替sychronized,因為volatile不能保證操作的原子性,即使只是i++,實際上也是由多個原子操作組成:read i; inc; write i,假如多個線程同時執行i++,volatile只 ? ?能保證他們操作的i是同一塊內存,但依然可能出現寫入臟數據的情況。如果配合Java 5增加的atomic wrapper classes,對它們的increase之類的操作就不需要sychronized。
總結一下,volatile可以修飾變量,用于多線程的訪問,但是在多核的情況下,多線程并發還是存在問題。所以最好使用sychronized。
android volatile的使用就講完了。
就這么簡單。
總結
以上是生活随笔為你收集整理的android volatile的使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android 动态修改控件的宽高
- 下一篇: android edittext 不可编