java 中的volatile關(guān)鍵字
1.volatile實(shí)現(xiàn)可見(jiàn)性的原理是什么?
有volatile
變量修飾的共享變量進(jìn)行寫操作的時(shí)候匯編代碼會(huì)多出一個(gè)Lock
前綴指令。
在該指令下,多核處理器會(huì)引發(fā)兩件事:
- 將當(dāng)前處理器緩存行的數(shù)據(jù)寫回系統(tǒng)內(nèi)存
- 這個(gè)寫回內(nèi)存的操作會(huì)使在其他CPU里緩存了該內(nèi)存地址的數(shù)據(jù)無(wú)效
這里需要簡(jiǎn)單了解CPU緩存一致性問(wèn)題:多核處理器環(huán)境下,每個(gè)CPU都有自己的緩存行,緩存了內(nèi)存中的數(shù)據(jù),要維護(hù)多個(gè)CPU中緩存的數(shù)據(jù)一致性,就需要解決兩個(gè)問(wèn)題:
- 一是寫傳播(某個(gè)CPU里的cache數(shù)據(jù)更新時(shí),需要傳播到其他CPU的cache中);
- 二是事務(wù)的串行化執(zhí)行(在某個(gè)CPU里對(duì)數(shù)據(jù)的修改,在其他CPU中看起來(lái)順序是一樣的,也就是要引入近似[鎖]的概念,保證同一時(shí)刻只有一個(gè)CPU可以對(duì)數(shù)據(jù)做修改);
寫傳播是通過(guò)[總線嗅探]完成的:通過(guò)總線把修改數(shù)據(jù)的事件廣播通知給其他所有的核心,每個(gè)CPU核心都會(huì)監(jiān)聽(tīng)總線上的廣播事件,并檢查是否有相同的數(shù)據(jù)在自己的Cache
里面;而事務(wù)的串行化則通過(guò)[MESI協(xié)議]來(lái)完成。
MESI
(Modified(已修改)、Exclusive
(獨(dú)占)、Shared
(共享)、Ivalidated
(已失效))協(xié)議中,如果要修改一個(gè)共享數(shù)據(jù),不能直接修改,要先向其他CPU廣播一個(gè)請(qǐng)求,把其他CPU cache中對(duì)應(yīng)的數(shù)據(jù)狀態(tài)改為Invalidated
;以后其他CPU在讀取標(biāo)記為Invalidated
的數(shù)據(jù)時(shí),需要強(qiáng)制從內(nèi)存中讀取數(shù)據(jù)。
2.演示volatile的可見(jiàn)性
public class VolatileDemo { static int flag = 1; // 定義一個(gè)共享變量 public static void main(String[] args) { // 兩個(gè)線程,一個(gè)線程負(fù)責(zé)讀取flag的值,另一個(gè)線程負(fù)責(zé)修改flag的值 new Thread(){ int localflag = flag; @Override public void run() { while(true){ //flag被修改后就跟localflag不一樣了 if(localflag!=flag){ System.out.println("讀到了flag修改后的值:"+ flag); //把讀到的值賦值給本地變量 localflag = flag; } } } }.start(); new Thread(){ int localflag = flag; @Override public void run() { while (true){ //一直對(duì)flag的值進(jìn)行修改 System.out.println("對(duì)flag的值進(jìn)行修改:"+ ++localflag); flag = localflag; //休眠一秒更好地觀察結(jié)果 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } }
可以看到另一個(gè)線程并不能及時(shí)讀取到被修改的值。
共享變量用volatile修飾后:
public class VolatileDemo { static volatile int flag = 1; public static void main(String[] args) { // 兩個(gè)線程,一個(gè)線程負(fù)責(zé)讀取flag的值,另一個(gè)線程負(fù)責(zé)修改flag的值 new Thread(){ int localflag = flag; @Override public void run() { while(true){ //flag被修改后就跟localflag不一樣了 if(localflag!=flag){ System.out.println("讀到了flag修改后的值:"+ flag); //把讀到的值賦值給本地變量 localflag = flag; } } } }.start(); new Thread(){ int localflag = flag; @Override public void run() { while (true){ //一直對(duì)flag的值進(jìn)行修改 System.out.println("對(duì)flag的值進(jìn)行修改:"+ ++localflag); flag = localflag; //休眠一秒更好地觀察結(jié)果 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } }
可以看到用volatile
修飾后,每次另一個(gè)線程總能讀取到修改后的值。
到此這篇關(guān)于java 中的volatile關(guān)鍵字的文章就介紹到這了,更多相關(guān)volatile關(guān)鍵字內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MyBatis注解開(kāi)發(fā)-@Insert和@InsertProvider的使用
這篇文章主要介紹了MyBatis注解開(kāi)發(fā)-@Insert和@InsertProvider的使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。2022-07-07關(guān)于@PostConstruct、afterPropertiesSet和init-method的執(zhí)行順序
這篇文章主要介紹了關(guān)于@PostConstruct、afterPropertiesSet和init-method的執(zhí)行順序,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09java框架基礎(chǔ)之SPI機(jī)制實(shí)現(xiàn)及源碼解析
這篇文章主要為大家介紹了java框架基礎(chǔ)之SPI機(jī)制實(shí)現(xiàn)及源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09