轉(zhuǎn)自小端有話說蒂窒;
[toc]
請(qǐng)你說下對(duì)Valotile的了解,以及使用場(chǎng)景 ?
分析:說到valotile, 我們應(yīng)該知道它的使用場(chǎng)景是多線程. 對(duì)于多線程編程,我們要解決的問題集中在三個(gè)方面:
- a.原子性,最簡(jiǎn)單的例子就是,i++,在多線程環(huán)境下笨使,最終的結(jié)果是不確定的,為什么僚害?就是因?yàn)檫@么一個(gè)++操作硫椰,被編譯為指令后,是多個(gè)指令來完成的萨蚕。那么遇到并發(fā)的情況靶草,就會(huì)導(dǎo)致彼此“覆蓋”的情況。
- b.可見性岳遥,通俗解釋就是奕翔,在A線程對(duì)一個(gè)變量做了修改,在B線程中浩蓉,能正確的讀取到修改后的結(jié)果派继。究其原理宾袜,是cpu不是直接和系統(tǒng)內(nèi)存通信,而是把變量讀取到L1驾窟,L2等內(nèi)部的緩存中庆猫,也叫作私有的數(shù)據(jù)工作棧。修改也是在內(nèi)部緩存中绅络,但是何時(shí)同步到系統(tǒng)內(nèi)存是不能確定的月培,有了這個(gè)時(shí)間差,在并發(fā)的時(shí)候恩急,就可能會(huì)導(dǎo)致杉畜,讀到的值,不是最新值假栓。
- c.有序性:這里只說指令重排序寻行,虛擬機(jī)在把代碼編譯為指令后執(zhí)行霍掺,出于優(yōu)化的目的匾荆,在保證結(jié)果不變的情況下,可能會(huì)調(diào)整指令的執(zhí)行順序杆烁。
valotile牙丽,能滿足上述的可見性和有序性。但是無(wú)法保證原子性
可見性兔魂,是在修改后烤芦,強(qiáng)制把對(duì)變量的修改同步到系統(tǒng)內(nèi)存。而其他cpu在讀取自己的內(nèi)部緩存中的值的時(shí)候析校,發(fā)現(xiàn)是valotile修飾的构罗,會(huì)把內(nèi)部緩存中的值,置為無(wú)效智玻,然后從系統(tǒng)內(nèi)存讀取遂唧。
有序性,是通過內(nèi)存屏障來實(shí)現(xiàn)的吊奢。所謂的內(nèi)存屏障盖彭,可以理解為,在某些指令中页滚,插入屏障指令召边,用以確保,在向屏障指令后面繼續(xù)執(zhí)行的時(shí)候裹驰,其前面的所有指令已經(jīng)執(zhí)行完畢隧熙。
既然說valotile 還無(wú)法保證線程安全, 那該如何線程安全 ?
這時(shí)候就需要同步器比如 synchronized 的介入, 來保證原子性了;
畢竟 原子性 + 可見性 + 有序性
才能保證線程安全
valotile在單例模式中的應(yīng)用
在寫單例模式時(shí),我們通常會(huì)采用雙層判斷的方式幻林,在最內(nèi)層贱鼻,instance = new Singleton()宴卖。其實(shí)這也有一個(gè)隱含的問題:這句賦值語(yǔ)句,其實(shí)是分三步來操作的:
a.為instance分配內(nèi)存
b.調(diào)用Singleto構(gòu)造函數(shù)來初始化變量
c.instance指向上一步初始化的對(duì)象
在jvm做了指令重排序優(yōu)化后邻悬,上述步驟b和c不能保證症昏,可能出現(xiàn),c先執(zhí)行父丰,但是對(duì)象卻沒初始化肝谭,這時(shí)候其他線程判斷的時(shí)候,發(fā)現(xiàn)是非null蛾扇,但是使用的時(shí)候攘烛,卻沒有具體實(shí)例,導(dǎo)致報(bào)錯(cuò)镀首。
所以坟漱,我們可以用valotile來修飾instance,避免該問題更哄。