问题
I'm studying Java Concurrency in Practice and there it is explained why the following snippet of code is bad:
public class NoVisibility {
private static boolean ready;
private static int number;
private static class ReaderThread extends Thread {
public void run() {
while (!ready) {
Thread.yield();
System.out.println(number);
}
}
public static void main(String[] args) {
new ReaderThread().start();
number = 42;
ready = true;
}
}
This code may print 0 or loop forever.
While it is easy to understand why NoVisibility
could print 0 instead of 42 (due to re-ordering issue),
I'm a little confused about the infinite loop.
What is a practical scenario where an infinite loop may occurr, in this code?
回答1:
The loop stops when ready
is set to true
. And ready
is set to true
by the main thread. But since the ready
field is not volatile, the looping thread might continue to see a cached value: false
.
The volatile
keyword guarantees that all the threads reading a volatile field will actually see the last value stored in this field by any other thread. Without volatile
, you don't have this guarantee.
回答2:
Well, it turns out that things are not nearly that easy: as you print in the cycle, you synchronize on the output streams, and that involves all the memory fences, so you will actually exit.
Now, you must not rely on such a behaviour, but that means if you're going to demonstrate the problem, you should be extra careful: many system calls, especially I/O, will involve hidden synchronization which can ruin a trick. So you end up saying, "that's baaaaad"— and being not able to prove that, which is a bit frustrating.
For a code example, check out illustrating volatile : is this code thread-safe? (sorry for a shameless plug—it's just that I only have this code there).
回答3:
Please see the below code, It introduces the Infinite loop pretty on x86. Tried with jdk8 and jdk7
package com.snippets;
public class SharedVariable {
private static int sharedVariable = 0;// declare as volatile to make it work
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
sharedVariable = 1;
}
}).start();
for(int i=0;i<1000;i++) {
for(;;) {
if(sharedVariable == 1) {
break;
}
}
}
System.out.println("Value of SharedVariable : " + sharedVariable);
}
}
Trick is not to expect the processor to do the reordering rather make compiler to make some optimization which introduces the visibility bug.
If you run the above code you will see it hangs indefinitely because it never sees the updated value sharedVariable.
To correct the code declare the sharedVariable as volatile.
Why normal variable didn't work and the above program hangs ?
- sharedVariable was not declared as volatile.
Now because sharedVariable was not declared as volatile compiler optimizes the code. It sees that sharedVariable is not going be changed so why i should read from memory every time in the loop. It will take the sharedVariable out of the loop. Something similar to below.
for(int i=0i<1000;i++)/**compiler reorders sharedVariable as it is not declared as volatile and takes out the if condition out of the loop which is valid as compiler figures out that it not gonna change sharedVariable is not going change **/ if(sharedVariable != 1) { for(;;) {} } }
Shared at github : https://github.com/lazysun/concurrency/blob/master/Concurrency/src/com/snippets/SharedVariable.java
来源:https://stackoverflow.com/questions/8768364/java-visibility-and-infinite-loop-occurrence