并發(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)該失效!