Rohit
Rohit

Reputation: 753

Values read in Multithreading environment

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

Answers (4)

jdb
jdb

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

Vishal K
Vishal K

Reputation: 13066

f.y could be seen as 0 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.

  1. T1 calls the method 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 T2
  2. T2 calls the method reader(). 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 the compiler a freedom to optimize the code in such a way that while execution the call to constructor is inlined, and the shared variable f shared among Threads T1 and T2 may immediately be updated once storage has been allocated but before the inlined constructor initializes the object.

Upvotes: 0

Gray
Gray

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

lbalazscs
lbalazscs

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

Related Questions