java volatile原理 多個(gè)線程可以讀一個(gè)變量,只有一個(gè)線程可以對(duì)這個(gè)變量進(jìn)行寫,到底要不要加鎖?
多個(gè)線程可以讀一個(gè)變量,只有一個(gè)線程可以對(duì)這個(gè)變量進(jìn)行寫,到底要不要加鎖?下面簡要解釋一下原因:鎖定是因?yàn)椴僮鞑皇窃拥?。讓我們用操作一來解釋它。看下面兩個(gè)圖。我這個(gè)操作需要看上面的第二個(gè)圖,你能很清
多個(gè)線程可以讀一個(gè)變量,只有一個(gè)線程可以對(duì)這個(gè)變量進(jìn)行寫,到底要不要加鎖?
下面簡要解釋一下原因:
鎖定是因?yàn)椴僮鞑皇窃拥?。讓我們用操作一來解釋它??聪旅鎯蓚€(gè)圖。
我這個(gè)操作需要
看上面的第二個(gè)圖,你能很清楚地理解這個(gè)過程嗎?
鎖定是為了確保上述三個(gè)步驟是原子操作。
回到問題上來,只有一個(gè)線程要寫,沒有競(jìng)爭(zhēng),所以不需要鎖定。
但是,如果你看第一張圖片,因?yàn)橹鲀?nèi)存和本地內(nèi)存的存在
在一個(gè)線程寫入后,其他線程無法立即看到它。這就是可見性問題。
添加volatile關(guān)鍵字后,它將在操作后強(qiáng)制工作內(nèi)存和主內(nèi)存同步,以確保其他線程可以立即看到它。
C 多線程有必要加volatile么?
1. Volatile與多線程無關(guān)。2添加volatile不會(huì)使錯(cuò)誤的程序正確,刪除volatile不會(huì)使正確的程序錯(cuò)誤。如果你愿意,你可以加上這個(gè)東西。無論如何,它將沒有其他影響,除了減慢你的程序。三。除了在處理硬件時(shí)有用外,volatile唯一適用的地方是與信號(hào)相關(guān)的代碼,例如:#include<unistd。H>#包含<信號(hào)。H>#包括<stdio。H>int*foo=nullvoid signalhandler(int x){*foo=20}int main(){//volatile int bar=10 int bar=10 int Baz=15 foo=&Baz foo//foo現(xiàn)在指向bar,希望。。。Signal(SIGINT,sighandler)sleep(10)printf(%dn“,bar)}
(我知道這個(gè)程序是UB,我只想解釋這個(gè)問題)如果沒有volatile,編譯器會(huì)假設(shè)bar的值保持不變。按ctrl-c時(shí),程序?qū)⑤敵?0。
為什么在多核多線程程序中要慎用volatile關(guān)鍵字?
由于volatile不能保證它的原子性,它只能保證一個(gè)線程在修改后對(duì)其他線程可見,特別是當(dāng)多個(gè)線程自動(dòng)激活并減少一個(gè)變量時(shí),會(huì)導(dǎo)致變量錯(cuò)誤。參考《深入理解Java虛擬機(jī)》一書,volatile用于以下場(chǎng)景:
1>操作結(jié)果不依賴于變量的當(dāng)前值,或者可以確保只有一個(gè)線程修改變量的值。
2>變量不需要與其他狀態(tài)變量一起參與不變約束。因此,在使用volatile關(guān)鍵字時(shí),應(yīng)該小心。不僅僅是簡單的類型變量被volatile修改。此變量上的所有操作都是原始操作。當(dāng)一個(gè)變量的值由它以前的值決定時(shí),例如n=n1,n volatile關(guān)鍵字將無效。只有當(dāng)一個(gè)變量的值與其前一個(gè)值無關(guān)時(shí),對(duì)該變量的操作才能是原子級(jí)的,例如n=m1,這是原始級(jí)別。因此,在使用volatile鍵時(shí)必須小心。如果不確定,可以使用synchronized而不是volatile。
volatile關(guān)鍵字在Java中有什么作用?
Volatile是為了防止指令重新排序以確??梢娦?/p>
對(duì)于JVM級(jí)別,是為了防止編譯器重新排序
同時(shí),對(duì)于某些CPU,它們會(huì)通過緩存鎖或線程來解決緩存可見性
但是,目前很多CPU已經(jīng)過優(yōu)化,由于cache一致性MESI會(huì)帶來性能開銷,因此采用storebuffer機(jī)制進(jìn)行異步處理,這種機(jī)制會(huì)導(dǎo)致指令的無序執(zhí)行。這會(huì)導(dǎo)致可見性問題。
然后volatile將在CPU級(jí)別增加內(nèi)存屏障,以解決由CPU無序執(zhí)行引起的可見性問題