volatile關(guān)鍵字解析

并發(fā)編程中的三個(gè)概念

原子性,可見性,有序性.

原子性:即一個(gè)操作或者多個(gè)操作 要么全部執(zhí)行并且執(zhí)行的過程不會(huì)被任何因素打斷,要么就都不執(zhí)行。

可見性是指當(dāng)多個(gè)線程訪問同一個(gè)變量時(shí),一個(gè)線程修改了這個(gè)變量的值,其他線程能夠立即看得到修改的值。

有序性:即程序執(zhí)行的順序按照代碼的先后順序執(zhí)行。

volatile關(guān)鍵字的兩層語義

一旦一個(gè)共享變量(類的成員變量、類的靜態(tài)成員變量)被volatile修飾之后,那么就具備了兩層語義:

1)保證了不同線程對這個(gè)變量進(jìn)行操作時(shí)的可見性,即一個(gè)線程修改了某個(gè)變量的值,這新值對其他線程來說是立即可見的。

2)禁止進(jìn)行指令重排序。

使用條件

兩個(gè)條件:

對變量的寫操作不依賴于當(dāng)前值。

該變量沒有包含在具有其他變量的不變式中。

volatile保證不了線程安全

想要線程安全必須保證原子性,可見性,有序性。而volatile只能保證可見性和有序性

緩存一致性協(xié)議

如果我寫入之后發(fā)現(xiàn)這是共享變量就使得其他cpu緩存了的值失效,讓它再次去內(nèi)存中讀取。

貼一段代碼

public class VolatileUnsafe {

? private static class VolatileVar implements Runnable {

? ? ? private volatile int a = 0;

? ? ? @Override

? ? ? public void run() {

? ? ? ? ? String threadName = Thread.currentThread().getName();

? ? ? ? ? a = a++;

? ? ? ? ? System.out.println(threadName+":======"+a);

? ? ? ? ? SleepTools.ms(100);

? ? ? ? ? a = a+1;

? ? ? ? ? System.out.println(threadName+":======"+a);

? ? ? }

}

? ? public static void main(String[] args) {

? ? ? VolatileVar v = new VolatileVar();

? ? ? ? Thread t1 = new Thread(v);

? ? ? ? Thread t2 = new Thread(v);

? ? ? ? Thread t3 = new Thread(v);

? ? ? ? Thread t4 = new Thread(v);

? ? ? ? t1.start();

? ? ? ? t2.start();

? ? ? ? t3.start();

? ? ? ? t4.start();

? ? }

}


?這樣如果有一個(gè)變量i = 0用volatile修飾,兩個(gè)線程對其進(jìn)行i++操作,如果線程1從內(nèi)存中讀取i=0進(jìn)了緩存,然后把數(shù)據(jù)讀入寄存器,之后時(shí)間片用完了,然后線程2也從內(nèi)存中讀取i進(jìn)緩存,因?yàn)榫€程1還未執(zhí)行寫操作,內(nèi)存屏障是插入在寫操作之后的指令,意味著還未觸發(fā)這個(gè)指令,所以緩存行是不會(huì)失效的。然后線程2執(zhí)行完畢,內(nèi)存中i=1,然后線程1又開始執(zhí)行,然后將數(shù)據(jù)寫回緩存再寫回內(nèi)存,結(jié)果還是1。

主要原因:

就是volatile只有再執(zhí)行寫操作的時(shí)候次才會(huì)緩存失效而不是讀時(shí)就應(yīng)該失效!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容