问题
Today I was creating one timeout job using TimerTask but fell in to a new problem where i have a static volatile boolean variable flag. My understanding is as soon as value of this variable get changed it is notified by all running thread. But when I ran this program I got below output which is not acceptable.
O/P:
--------------
--------------
DD
BB
Exiting process..
CC
My expectation is my last print should be Exiting process.. Why is this strange behavior?
My code is:
public class TimeOutSort {
static volatile boolean flag = false;
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
flag = true;
System.out.println("Exiting process..");
// System.exit(0);
}
}, 10 * 200);
new Thread(new Runnable() {
@Override
public void run() {
while (!flag)
System.out.println("BB");
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (!flag)
System.out.println("CC");
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (!flag)
System.out.println("DD");
}
}).start();
}
}
Edit: How can i achieve this ?
回答1:
volatile pretty much means that each time a thread accesses a variable it must ensure to use the version visible to each thread (i.e. no per-thread caching).
This doesn't force the CC-printing thread to actually get to run immediately after the flag has been set to true. It's entirely possible (especially on a single-core machine) that one thread sets the flag and prints the message before the CC-printing thread even had a chance to run.
Also: note that printing to System.out involves acquiring a lock (somewhere inside the println() call), which can modify the multi-threaded behaviour of test code.
回答2:
Threads can execute code in any order
thread BB: while (!flag) // as flag is false
thread Main: flag = true;
thread Main: System.out.println("Exiting process..");
thread BB: System.out.println("BB");
My expectation is my last print should be Exiting process..
Threads are designed to run concurrently and independently. It would be surprising if this was always the last statement because you can't be sure where each thread is when you set the flag.
回答3:
The thread that prints "CC" happened not to receive any CPU time until after your thread that prints "Exiting process..." printed that. This is expected behavior.
回答4:
It's not volatile not working (if it were not, some of your threads would not have stopped). It's about the order of execution of instructions in the different threads, and this is random (depends on OS scheduling) unless you explicitly synchronize the loops at intermediate steps.
回答5:
To add an alternative phrasing to the explanations you got: in your sample output, the thread that prints "CC" got suspended (somewhere) "between" the lines while (!flag) and System.out.println(). Which means that after it wakes up, the println() executes before the next check of the flag. (It also won't get woken up just because you change the flag value, but because some other thread blocks or uses up its time slice.)
回答6:
i didn't test it , but you may achieve it like this
public class TimeOutSort {
static volatile boolean flag = false;
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
synchronized(flag){
flag = true;
notifyAll();
}
}
}, 10 * 200);
new Thread(new Runnable() {
@Override
public void run() {
synchronized(flag){
if(!flag)
{
wait();
}
System.out.println("BB");
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized(flag){
if(!flag)
{
wait();
}
System.out.println("CC");
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized(flag){
if(!flag)
{
wait();
}
System.out.println("DD");
}
}
}).start();
}
}
来源:https://stackoverflow.com/questions/11670530/why-volatile-is-not-working-properly