請看以下代碼
public class TestVolatile {
public static void main(String[] args) throws InterruptedException {
ThreadDemo td = new ThreadDemo();
new Thread(td).start();
Thread.sleep(1);
while(true){
if(td.isFlag()){
System.out.println("------------------");
break;
}
}
}
}
class ThreadDemo implements Runnable {
private boolean flag = false;
@Override
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
flag = true;
System.out.println("flag=" + isFlag());
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
把Thread.sleep(1)
換成Thread.sleep(1000)
就能獲取flag
修改后的值,即td.isFlag()
返回true
。
雖然看了Java內(nèi)存模型的概念,但我不知道如何解釋這段代碼,誰能解釋一下?
相關(guān)問題: Java多線程的工作內(nèi)存是什么?
這個期待是沒有規(guī)范支撐的。代碼中沒有做任何能保證 "子線程寫 happen-before 主線程讀" 的事情。
sleep(1000)
后看到修改只是巧合,一個JVM如果在更久后才讓主線程看到,甚至永遠(yuǎn)不讓主線程看到都不違反規(guī)范。
你的程序應(yīng)該是想測試 volatile
關(guān)鍵字的功能。但是 “把 Thread.sleep(1)
換成 Thread.sleep(1000)
就能獲得預(yù)期效果” 這樣做理解上是不對的。
首先,程序中總共有兩個線程,主線程(暫稱 線程M)和 new Thread(td)
(暫稱 線程T)。
當(dāng)寫 Thread.sleep(1)
的時候,線程M 在 1ms 之后,便開始在 while(true)
循環(huán)中檢查 td.isFlag()
的值,但是因為內(nèi)存可見性的關(guān)系,線程M 并不能及時讀取 線程T 中 flag 的值,所以此時導(dǎo)致了死循環(huán);
當(dāng)寫 Thread.sleep(1000)
的時候,M 在 1000ms 之后,開始在 while(true)
循環(huán)中檢查 td.isFlag()
的值;但是 T 在 200ms 的時候,便將 Thread.sleep(1000)
的時候,M 在 1000ms 之后,開始在 while(true)
循環(huán)中檢查 td.isFlag()
的值;但是 T 在 200ms 的時候,便將 flag 的值設(shè)為 true
了,所以,M 在 1000ms 之后檢測 td.isFlag()
的值肯定是返回 true
的,那么第一次判斷便會返回 true
,產(chǎn)生輸出并跳出 while(true)
flag
true
了,所以,M 在 1000ms 之后檢測 td.isFlag()
的值肯定是返回 true
的,那么第一次判斷便會返回 true
,產(chǎn)生輸出并跳出 while(true)
循環(huán)。
為了讓 線程M 及時讀取到 線程T 中 flag 的值,需要將 flagvolatile
使用
private volatile boolean flag = false;
那么每次對 flagvolatile
的修改,其他線程都立馬可見。關(guān)于 的使用,可以參考我的博客:Java 多線程(6):volatile 關(guān)鍵字的使用??可以參考如下三個代碼:
其中第一個和你的情況一樣,由于多線程的可見性問題,可能導(dǎo)致無限循環(huán)下去。
第二個是使用synchronized
解決此問題,大多數(shù)工作場景用這個好synchronized
解決此問題,大多數(shù)工作場景用這個好
第三個是使用volatile
第三個是使用volatile
解決,但這個關(guān)鍵字只保證可見性,在實際場景中局限性比較大,得慎用
public class StopThread {
private static boolean stopRequested;
public static void main(String[] args) throws InterruptedException {
Thread backgroundThread = new Thread(new Runnable() {
@Override
public void run() {
@SuppressWarnings("unused")
int i = 0;
while(!stopRequested) {
// System.out.println("加上這一句程序就可以終止,否則無限循環(huán)下去");
i++;
}
}
});
backgroundThread.start();
TimeUnit.SECONDS.sleep(1);
stopRequested = true;
}
}
public class StopThread2 {
private static boolean stopRequested;
public static synchronized boolean getStopRequested() {
return stopRequested;
}
public static synchronized void requestStop() {
stopRequested = true;
}
public static void main(String[] args) throws InterruptedException {
Thread backgroundThread = new Thread(new Runnable() {
@Override
public void run() {
@SuppressWarnings("unused")
int i = 0;
while(!getStopRequested()/* stopRequested */) {
i++;
}
}
});
backgroundThread.start();
TimeUnit.SECONDS.sleep(1);
requestStop();/* stopRequested = true; */
}
}
public class StopThread3 {
private static volatile boolean stopRequested;
public static void main(String[] args) throws InterruptedException {
Thread backgroundThread = new Thread(new Runnable() {
@Override
public void run() {
@SuppressWarnings("unused")
int i = 0;
while(stopRequested) {
i++;
}
}
});
backgroundThread.start();
TimeUnit.SECONDS.sleep(1);
stopRequested = true;
}
}