引入的原因:
- 線(xiàn)程間通信可以使用共享變量的方式(一塊公共內(nèi)存)
- 現(xiàn)代計(jì)算機(jī)都是多cpu的奔缠,并且為了提高效率掠抬,cpu都是帶緩存的,并且設(shè)計(jì)成分層的校哎,有一級(jí)緩存两波、二級(jí)緩存、三級(jí)緩存(L1闷哆,L2腰奋,L3)。每個(gè)cpu有自己的cache
L1響應(yīng)時(shí)間1ns抱怔,3個(gè)時(shí)鐘周期劣坊,大小32K
L2響應(yīng)時(shí)間3ns,大小256K
L3響應(yīng)時(shí)間12ns屈留,大小8M
直接訪問(wèn)主內(nèi)存的響應(yīng)時(shí)間65ns
為什么設(shè)計(jì)這么復(fù)雜局冰,還分層?
就是考慮成本的問(wèn)題灌危,最近也看到新聞說(shuō)內(nèi)存漲價(jià)了康二,如果不考慮成本那就簡(jiǎn)單了,直接都懟上最貴的L1cache勇蝙,就不需要開(kāi)發(fā)人員考慮這么多細(xì)節(jié)了沫勿。
實(shí)際上一些應(yīng)用程序最熱的數(shù)據(jù)也就是那么幾K,分層的目的也就是這個(gè)原因味混。
有以上是背景产雹,在兩個(gè)線(xiàn)程訪問(wèn)的時(shí)候,如果是不同的cpu執(zhí)行這兩個(gè)線(xiàn)程翁锡,就會(huì)出現(xiàn)緩存不一致問(wèn)題洽故。
volatile的作用
- 被volatile修飾的變量,jvm會(huì)做一些底層的工作盗誊,保證寫(xiě)入的操作,把更改后的內(nèi)容立刻從cpu緩存回寫(xiě)到主內(nèi)存隘弊。
- 保證了讀的可見(jiàn)性哈踱,當(dāng)一個(gè)線(xiàn)程讀到被volatile修飾的變量的時(shí)候,會(huì)拋棄cpu緩存的值梨熙,從主內(nèi)存復(fù)制一份到cpu緩存內(nèi)开镣,讀取。保證讀取得是最新的值咽扇。
synchronized和volatile作用的異同
當(dāng)使用synchronized加鎖的方式邪财,也可以實(shí)現(xiàn)多線(xiàn)程下可見(jiàn)性陕壹。還能保證寫(xiě)入的原子性。
但是volatile不能保證多線(xiàn)程下操作的原子性树埠。如下面例子
public class Test {
public volatile int inc = 0;
public void increase() {
inc++;
}
public static void main(String[] args) {
final Test test = new Test();
for(int i=0;i<10;i++){
new Thread(){
public void run() {
for(int j=0;j<1000;j++)
test.increase();
};
}.start();
}
while(Thread.activeCount()>1) //保證前面的線(xiàn)程都執(zhí)行完
Thread.yield();
System.out.println(test.inc);
}
}
要保證操作的原子性糠馆,處理synchronized關(guān)鍵字和lock外,還可以使用concurrent包下的AtomicInteger原子操作類(lèi)怎憋,原來(lái)是利用硬件的原子操作指令cap又碌。
volatile使用場(chǎng)景
volatile boolean inited = false;
//線(xiàn)程1:
context = loadContext();
inited = true;
//線(xiàn)程2:
while(!inited ){
sleep()
}
doSomethingwithconfig(context);
不使用volatile,會(huì)有概率出錯(cuò)绊袋。
沒(méi)有使用鎖是因?yàn)楸显龋瑅olatile性能更高。