日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

android volatile的使用

發(fā)布時(shí)間:2024/4/15 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android volatile的使用 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

今天,簡(jiǎn)單講講android里的volatile的使用。


這個(gè)其實(shí)很簡(jiǎn)單,而且我基本沒有用到,但是還是記錄一下。volatile的作用基本和sychronized相似,但是不能替代sychronized。

? volatile用處說明

? ? 在JDK1.2之前,Java的內(nèi)存模型實(shí)現(xiàn)總是從主存(即共享內(nèi)存)讀取變量,是不需要進(jìn)行特別的注意的。而隨著JVM的成熟和優(yōu)化,現(xiàn)在在多線程環(huán)境下volatile關(guān)鍵字的使用變得非常重要。

在當(dāng)前的Java內(nèi)存模型下,線程可以把變量存在本地內(nèi)存(比如機(jī)器的寄存器)中,而不是直接在主存中進(jìn)行讀寫。這就可能造成一個(gè)線程在主存中修改了一個(gè)變量的值,而另外一個(gè)線程還繼續(xù)使用它在寄存器中的變量值的拷貝,造成數(shù)據(jù)的不一致。


要解決這個(gè)問題,就需要把變量聲明為volatile(也可以使用同步,參見http://blog.csdn.net/ns_code/article/details/17288243),這就指示JVM,這個(gè)變量是不穩(wěn)定的,每次使用它都到主存中進(jìn)行讀取。一般說來,多任務(wù)環(huán)境下,各任務(wù)間共享的變量都應(yīng)該加volatile修飾符。 Volatile修飾的成員變量在每次被線程訪問時(shí),都強(qiáng)迫從共享內(nèi)存中重讀該成員變量的值。而且,當(dāng)成員變量發(fā)生變化時(shí),強(qiáng)迫線程將變化值回寫到共享內(nèi)存。這樣在任何時(shí)刻,兩個(gè)不同的線程總是看到某個(gè)成員變量的同一個(gè)值。 Java語言規(guī)范中指出:為了獲得最佳速度,允許線程保存共享成員變量的私有拷貝,而且只當(dāng)線程進(jìn)入或者離開同步代碼塊時(shí)才將私有拷貝與共享內(nèi)存中的原始值進(jìn)行比較。 這樣當(dāng)多個(gè)線程同時(shí)與某個(gè)對(duì)象交互時(shí),就必須注意到要讓線程及時(shí)的得到共享成員變量的變化。而volatile關(guān)鍵字就是提示JVM:對(duì)于這個(gè)成員變量不能保存它的私有拷貝,而應(yīng)直接與共享成員變量交互。 volatile是一種稍弱的同步機(jī)制,在訪問volatile變量時(shí)不會(huì)執(zhí)行加鎖操作,也就不會(huì)執(zhí)行線程阻塞,因此volatilei變量是一種比synchronized關(guān)鍵字更輕量級(jí)的同步機(jī)制。 使用建議:在兩個(gè)或者更多的線程需要訪問的成員變量上使用volatile。當(dāng)要訪問的變量已在synchronized代碼塊中,或者為常量時(shí),沒必要使用volatile。 由于使用volatile屏蔽掉了JVM中必要的代碼優(yōu)化,所以在效率上比較低,因此一定在必要時(shí)才使用此關(guān)鍵字



這個(gè)基本就是volatile的使用,可以修飾變量,當(dāng)多線程訪問時(shí),直接訪問變量的內(nèi)存而不是緩存,可以使用與多線程并發(fā)。但是存在問題,下面說一下:

volatile為什么不能保證原子性?

現(xiàn)在我們的手機(jī)都是多核的,也就是說同時(shí)有好幾顆CPU在工作,每顆CPU都有自己的Cache高速緩存,因?yàn)镃PU的速度特別快,而內(nèi)存的讀取操作相對(duì)于CPU的運(yùn)算速度來說很慢,所以就會(huì)拖累CPU的效率,引入Cache就是為了解決這個(gè)問題的,CPU先把需要的數(shù)據(jù)從內(nèi)存中讀到Cache中,然后直接和Cache來打交道,Cache的速度很快,因此可以保證CPU的工作效率,當(dāng)Cache中的數(shù)據(jù)改變后,再將被改變的數(shù)據(jù)寫回內(nèi)存中。


首先我們分析一下多線程在訪問一個(gè)普通的(沒有加volatile修飾符)的變量的過程
1.CPU1和CPU2先將count變量讀到Cache中,比如內(nèi)存中count=1,那么現(xiàn)在兩個(gè)CPU中Cache中的count都等于1
2.CPU1和CPU2分別開啟一個(gè)線程來對(duì)count進(jìn)行自增操作,每個(gè)線程都有一塊自己的獨(dú)立內(nèi)存空間來存放count的副本。
3.線程1對(duì)副本count進(jìn)行自增操作后,count=2 ; 線程2對(duì)副本count進(jìn)行自增操作后,count=2
4.線程1和線程2將操作后的count寫回到Cache緩存的count中
5.CPU1和CPU2將Cache中的count寫回內(nèi)存中的count。
那么問題就來了,線程1和線程2操作的count都是一個(gè)count副本,這兩個(gè)副本的值是一樣的,所以進(jìn)行了兩次自增后,寫回內(nèi)存的count=2。而正確的結(jié)果應(yīng)該為count=3。這就是多線程并發(fā)所帶來的問題


如果變量count用volatile修飾了可以解決這個(gè)問題嗎?

如果一個(gè)變量加了volatile關(guān)鍵字,就會(huì)告訴編譯器和JVM的內(nèi)存模型:這個(gè)變量是對(duì)所有線程共享的、可見的,每次JVM都會(huì)讀取最新寫入的值并使其最新值在所有CPU可見。我們?cè)賮砜匆幌戮€程在訪問一個(gè)加了volatile修飾符的變量的過程


當(dāng)count用volatile關(guān)鍵字修飾后,CPU1對(duì)count的值更新后,在寫回內(nèi)存的同時(shí)會(huì)通知CPU2 count值已經(jīng)更新了,你需要從內(nèi)存中獲取count最新的值!

注意:這里說CPU1通知CPU2其實(shí)是不嚴(yán)謹(jǐn)?shù)?#xff0c;其實(shí)這是緩存一致性機(jī)制在其作用,緩存一致性機(jī)制會(huì)阻止同時(shí)修改由兩個(gè)以上處理器緩存的內(nèi)存區(qū)域數(shù)據(jù),當(dāng)其他處理器回寫已被鎖定的緩存行數(shù)據(jù)時(shí),會(huì)使緩存行無效,當(dāng)CPU1將新數(shù)據(jù)寫回內(nèi)存后,會(huì)修改該數(shù)據(jù)在內(nèi)存中的內(nèi)存地址,CPU2通過嗅探在總線上傳播的數(shù)據(jù)來檢查自己的緩存行對(duì)應(yīng)的內(nèi)存地址是否被修改,如果被修改則將CPU2的該數(shù)據(jù)緩存行設(shè)置成無效狀態(tài),當(dāng)處理器對(duì)這個(gè)數(shù)據(jù)進(jìn)行修改操作的時(shí)候,會(huì)重新從系統(tǒng)內(nèi)存中把數(shù)據(jù)讀到CPU2的緩存行里。其實(shí)并不是CPU1通知CPU2,而是CPU2自己去嗅探。

其實(shí)大家只要明白了原理,怎么說也無所謂,就像好多地方都說volatile修飾的變量,線程直接和內(nèi)存交互,不會(huì)保存副本。而實(shí)際上線程還是會(huì)保存副本,只不過CPU每次都會(huì)從內(nèi)存中拿到最新的值,并且改變數(shù)據(jù)之后立馬寫回內(nèi)存,看上去就像線程直接和內(nèi)存交互一樣。

然后CPU2中的線程如果需要使用到count的時(shí)候,就會(huì)再從內(nèi)存中讀取count的值來更新自己的Cache。這看上去似乎解決了我們的問題,其實(shí)問題依然存在,我們來分析一下:
比如當(dāng)前count=1,CPU1和CPU2的Cache中的count都等于1,CPU1中的線程1對(duì)count進(jìn)行了自增操作,然后CPU1更新了內(nèi)存中count的值,并且通知CPU2 count的值已經(jīng)改變,然后CPU2從內(nèi)存中將count=2讀到了Cache中,并且線程2開始執(zhí)行count的自增操作,而就在CPU2剛剛將count的值讀回Cache的時(shí)候,CPU1又更新了count的值,此時(shí)count=3,并且通知CPU2,但是此時(shí)線程2已經(jīng)開始執(zhí)行了,線程2已經(jīng)將count=2拷貝到自己的內(nèi)存空間中了,所以即使CPU2再次更新自己Cache中的count=3,也來不及了,線程2操作的是他自己內(nèi)存空間中的count副本,所以線程2給count做完自增操作后,將count=3并且寫回Cache,CPU2更新內(nèi)存中的count。此時(shí)count的值應(yīng)該是4,然而CPU2更新完count的值后仍然等于3,這樣就出現(xiàn)了錯(cuò)誤。我們考慮的是只有兩顆CPU的情況,但是現(xiàn)在市面上已經(jīng)有8核手機(jī)了!如果8顆CPU同時(shí)工作的話,錯(cuò)誤會(huì)更嚴(yán)重!


Volatile一般情況下不能代替sychronized,因?yàn)関olatile不能保證操作的原子性,即使只是i++,實(shí)際上也是由多個(gè)原子操作組成:read i; inc; write i,假如多個(gè)線程同時(shí)執(zhí)行i++,volatile只 ? ?能保證他們操作的i是同一塊內(nèi)存,但依然可能出現(xiàn)寫入臟數(shù)據(jù)的情況。如果配合Java 5增加的atomic wrapper classes,對(duì)它們的increase之類的操作就不需要sychronized。


總結(jié)一下,volatile可以修飾變量,用于多線程的訪問,但是在多核的情況下,多線程并發(fā)還是存在問題。所以最好使用sychronized。


android volatile的使用就講完了。


就這么簡(jiǎn)單。

總結(jié)

以上是生活随笔為你收集整理的android volatile的使用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。