There is my code
public class Test {
private static boolean running = true;
public static void main(String[] args) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "get running:" + running);
while (running) {
}
System.out.println(Thread.currentThread().getName() + "end");
}, "t1").start();
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
running = false;
System.out.println(Thread.currentThread().getName() + "change running to:" + running);
}, "t2").start();
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "get running:" + running);
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "get running:" + running);
}, "t3").start();
}
}
console output
t1get running:true
t3get running:true
t2change running to:false
t3get running:false
so the thread t1
is stuck in while loop. And I know if I change running
to private static volatile boolean running
can fix this.
I question is t3
and t2
are different thread to, why t3
can get the new value of running
but t1
can't
EDIT1
@andrew-tobilko said it may because I call Thread.sleep
in the loop body, so I change the code of t3
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "get running:" + running);
long start = System.nanoTime();
long now = System.nanoTime();
while ((now-start)<3*1000L*1000L*1000L){
now = System.nanoTime();
}
System.out.println(Thread.currentThread().getName() + "get running:" + running);
}, "t3").start();
the result still the same
When you have a variable which you want to use to communicate between threads, then mark that variable as volatile:
private static volatile boolean running = true;
The reason is that threads are allowed to cache variables thread-locally for performance reasons or do other performance optimizations which only work on the assumption that the value of a variable won't change in a specific section of code. But the Java compiler and runtime won't be aware that another thread might change that variable.
Adding the volatile
modifier to a variable prevents those optimization and forces the optimizer to assume that this variable can change at any time for any reason.
It seems like TimeUnit.SECONDS.sleep(3);
in the third thread helps to reload running
rather than taking it from the cache in registers.
Note that the compiler doesn't have to do so.
In particular, the compiler does not have to flush writes cached in registers out to shared memory before a call to Thread.sleep or Thread.yield, nor does the compiler have to reload values cached in registers after a call to Thread.sleep or Thread.yield.
https://docs.oracle.com/javase/specs/jls/se13/html/jls-17.html#jls-17.3
So it's up to the compiler (and to the optimisations it might do).
For example, in the following (broken) code fragment, assume that this.done is a non-volatile boolean field:
while (!this.done) Thread.sleep(1000);
The compiler is free to read the field this.done just once, and reuse the cached value in each execution of the loop. This would mean that the loop would never terminate, even if another thread changed the value of this.done.
If you put a sleep
statement inside the loop in the first thread, it might also get a new value of running
. Again, no guarantee.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With