1.禁止指令重排序
``
public class VoliatlieTest3 {
private volatile VoliatlieTest3 singleInstace;
private VoliatlieTest3() {
}
public VoliatlieTest3 getSingleInstace() {
if (singleInstace == null) {
synchronized (VoliatlieTest3.class) {
if (singleInstace == null) {
singleInstace = new VoliatlieTest3();
}
}
}
return singleInstace;
}
}
``
singleInstace = new VoliatlieTest3()
這個代碼分3個指令:
1.分配內(nèi)存空間
2.初始化對象
3.將singleTon指向分配的內(nèi)存
第二步和第三步可能會次序不一樣
那么多線程這里會出問題
現(xiàn)在有線程A,B
如果A線程運行到singleInstace = new VoliatlieTest3(),并且這里指令被重新排序了,現(xiàn)在執(zhí)行執(zhí)行了
1.分配內(nèi)存空間
2.將singleInstace指向分配的內(nèi)存
執(zhí)行到這里的時候singleInstace就!= null了,但實際還是空的
這個時候線程B正好過來了,判斷singleInstace 不為空,然后返回了,此時B線程返回的就是空的對象
2.保證對象(基本數(shù)據(jù)類型)可見性,
``
public class VoliatlieTest2 extends Thread {
//這里 volatile 會使線程內(nèi)存中的變量從主內(nèi)存中獲取flat,如果沒有這個關(guān)鍵字,則下面的進程不會退出,每次打印的i都是不一樣的;
volatile boolean flat = false;
int i = 0;
@Override
public void run() {
while (!flat) { i++; }
}
public static void main(String[] args) {
VoliatlieTest2 voliatlieTest2 = new VoliatlieTest2();
voliatlieTest2.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
voliatlieTest2.flat = true;
System.out.println(voliatlieTest2.i);
System.out.println(voliatlieTest2.i);
}
}
``
主線程里面,flat 初始值是false, i初始值是0
子線程voliatelieTest2啟動起來后,如果flat是false對i進行++,主線程休眠1秒主線程將flat改為true
打印2次i的變量
這里雖然將flat改為true了,但是子線程還會對執(zhí)行i++,可以理解為子線程操作的是自己內(nèi)存中的那個flat, 沒有和主線程中的flat進行同步,主線程的flat被改為true了 但是子線程讀取的flat還是flase,如果flat用volatile修飾,主線程將flat改為true后,子線程讀取的flat就會是true 也就是i++就不會再執(zhí)行了,打印的值就是相同的數(shù)