Reputation: 753
I was going through the JLS documentation on Thread and Locks http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5 .
class FinalFieldExample {
final int x;
int y;
static FinalFieldExample f;
public FinalFieldExample() {
x = 3;
y = 4;
}
static void writer() {
f = new FinalFieldExample();
}
static void reader() {
if (f != null) {
int i = f.x; // guaranteed to see 3
int j = f.y; // could see 0
}
}
}
I am confused with above example (ex no 17.5-1) mentioned in the section as to how f.y could be seen as zero. The Reader Threads will either read the object f as null in which case it will not execute anything or it will read the object f with some reference. If the object f has a reference then the constructor must have completed its execution even though Multiple Writer Threads are running so that a reference could be assigned to f and if the constructor has executed then f.y should be seen as 4.
In what condition can f.y =0 be possible?
Thanks
Upvotes: 7
Views: 201
Reputation: 4509
It is not only instruction reordering. It can happen even if the write to f.y is before the one to f. The objects and the classes are all smoke and mirrors for humans. At CPU level it is all load memory location and store memory location. The data goes initially to the CPU cache. Lets assume that in this case f goes to one cache line and f.y to a different one. The writer thread executes something else to make the first cache line (holding f) visible to the rest of the CPUs. The one holding f.y is not yet visible (nothing instructed the CPU to do that). The memory location is still 0. When the reader thread runs on a different CPU it will load the memory location since nothing tells the CPU that this location has a pending change in another CPU cache. That means the second CPU will load f and f.y from memory. f holds the latest value but the latest f.y is still in the cache so the memory location holds 0. By putting volatile, final, etc. you are actually telling the compiler to generate code telling the CPUs to publish the data. And that is just one example, there are more.
Upvotes: 1
Reputation: 13066
f.y
could be seen as0
in following way:
Suppose two threads
T1 and T2 are running. T1 is accessing the method writer
while T2 is accession the method reader
of the same object of FinalFieldExample
.
writer()
. f.x
is already initialized to 3 because it is declared final which makes it compile time constant so it is initialized to given value during initialization of class FinalFieldExample
which happens before the creation of class Instance. Since f
is not declared as volatile
so the compiler reshuffles the sequence of steps in creation of object in following way: (a)f
is declared as not null (b)object of FinalFieldExample
is created (b) f
is reference to that object. But after point (a) is executed by T1 , T1 is preempted by T2reader()
. It finds f
to be not null so it goes within the if block. f.x
is already initialized to 3
so i
is assigned value 3
. But f.y
is till now initialized to default value 0
because the construction of object is not completed in step 1 given above. So j
is assigned value 0 .So we see that not declaring the variable
f
to be volatile gives thecompiler
a freedom to optimize the code in such a way that while execution the call toconstructor
is inlined, and the shared variablef
shared among ThreadsT1
andT2
may immediately be updated once storage has been allocated but before the inlined constructor initializes the object.
Upvotes: 0
Reputation: 116878
In what condition can f.y =0 be possible?
The Java memory model allows the JIT compiler to reorder initialization of non final fields outside the constructor. The x
field is final so it must be initialized by the JVM but y
is not final. So there is the possibility that the FinalFieldExample
is allocated and set on static FinalFieldExample f
but the initialization of the y
field has not been completed.
To quote from 17.5-1:
Because the writer method writes f after the object's constructor finishes, the reader method will be guaranteed to see the properly initialized value for f.x: it will read the value 3. However, f.y is not final; the reader method is therefore not guaranteed to see the value 4 for it.
Because f.y
is not final there is no guarantee that it has been set by the time the constructor finishes and the static f
is assigned. So there is a race condition created and the reader
may see y
as 3 or 0 depending on this race.
Upvotes: 5
Reputation: 17784
If a thread writes to a variable and another thread reads it, it is possible that the second thread does not see the new value even if the read happens later in time. This could happen for example if the two threads are executing on different processors, and the written value is cached in local processor registers.
The Java specification makes this non-intuitive behavior possible in order to improve performance (if this was not possible, then the processors could not use their local memory)
So whenever you read about the "happens-before" relationship in the Java Memory Model, remember that a "physical" happens-before is not necessarily a happens-before in the program logic. You need to establish explicitly the "happens-before" relationship between two threads, for example by synchronizing, by using volatile variables or in this case by using final variables.
Upvotes: 2