Please see the following code
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;
}
}
Replace Thread.sleep(1)
with Thread.sleep(1000)
to get the modified value of flag
, which is td .isFlag()
returnstrue
.
Although I have read the concept of Java memory model, I don't know how to explain this code. Who can explain it?
Related questions: What is the working memory of Java multi-threading?
You need to first tell me what your expected effect is? Ask questions clearly
This expectation is not supported by standards. Nothing is done in the code to guarantee that "subthread writes happen-before main thread reads".
sleep(1000)
Seeing the modification later is just a coincidence. If a JVM waits longer for the main thread to see it, or even never lets the main thread see it, it does not violate the specification.
Your program should be tested volatile
關(guān)鍵字的功能。但是 “把 Thread.sleep(1)
換成 Thread.sleep(1000)
就能獲得預(yù)期效果” 這樣做理解上是不對的。
首先,程序中總共有兩個線程,主線程(暫稱 線程M)和 new Thread(td)
(tentatively called thread T).
When writing the value of Thread.sleep(1)
的時候,線程M 在 1ms 之后,便開始在 while(true)
循環(huán)中檢查 td.isFlag()
, due to memory visibility, thread M cannot read the value of flag in thread T in time, so an infinite loop is caused at this time;
When writing Thread.sleep(1000)
, M starts to check td.isFlag()
in the while(true)
loop after 1000ms. code>; but T sets the value of 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
at 200ms, so M detects td.isFlag()
after 1000ms The value must return true
, then the first judgment will return true
, generate output and jump out of the while(true)
loop.
In order for thread M to read the value of flag in thread T in time, flagvolatile
needs to be modified with the
private volatile boolean flag = false;
Then every modification to flagvolatile
will be immediately visible to other threads. Regarding the use of , you can refer to my blog: Java Multithreading (6): Use of volatile keyword ??You can refer to the following three codes:
The first one is the same as your situation. Due to the visibility problem of multi-threading, it may cause an infinite loop.
The second is to use synchronized
to solve this problem. This is good for most work scenarios. synchronized
解決此問題,大多數(shù)工作場景用這個好
第三個是使用volatile
The third is to use volatile
to solve this problem, but this keyword only guarantees visibility. In actual scenarios, the limitations are relatively large, so use it with caution
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;
}
}