Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Volatile guarantees and out-of-order execution [duplicate]

IMPORTANT EDIT I know about the "happens before" in the thread where the two assignments are happening my question is would it be possible for another thread to be reading "b" non-null while "a" is still null. So I know that if you're calling doIt() from the same thread as the one where you previously called setBothNonNull(...) then it cannot throw a NullPointerException. But what if one is calling doIt() from another thread than the one calling setBothNonNull(...) ?

Note that this question is solely about the volatile keyword and the volatile guarantees: it is not about the synchronized keyword (so please don't answer "you must use synchronize" for I don't have any issue to solve: I simply want to understand the volatile guarantees (or lack of guarantees) regarding out-of-order execution).

Say we have an object containing two volatile String references that are initialized to null by the constructor and that we have only one way to modify the two String: by calling setBoth(...) and that we can only set their references afterwards to non-null reference (only the constructor is allowed to set them to null).

For example (it's just an example, there's no question yet):

public class SO {      private volatile String a;     private volatile String b;      public SO() {         a = null;         b = null;     }      public void setBothNonNull( @NotNull final String one, @NotNull final String two ) {         a = one;         b = two;     }      public String getA() {         return a;     }      public String getB() {         return b;     }  } 

In setBothNoNull(...), the line assigning the non-null parameter "a" appears before the line assigning the non-null parameter "b".

Then if I do this (once again, there's no question, the question is coming next):

doIt() {     if ( so.getB() != null ) {         System.out.println( so.getA().length );     } } 

Am I correct in my understanding that due to out-of-order execution I can get a NullPointerException?

In other words: there's no guarantee that because I read a non-null "b" I'll read a non-null "a"?

Because due to out-of-order (multi)processor and the way volatile works "b" could be assigned before "a"?

volatile guarantees that reads subsequent to a write shall always see the last written value, but here there's an out-of-order "issue" right? (once again, the "issue" is made on purpose to try to understand the semantics of the volatile keyword and the Java Memory Model, not to solve a problem).

like image 790
SyntaxT3rr0r Avatar asked Mar 14 '10 05:03

SyntaxT3rr0r


People also ask

Does volatile prevent reordering?

Reading and writing of volatile variables causes the variable to be read or written to main memory. Reading from and writing to main memory is more expensive than accessing the CPU cache. Accessing volatile variables also prevent instruction reordering which is a normal performance enhancement technique.

What guarantee volatile variable provides?

It guarantees that value of the volatile variable will always be read from the main memory, not from the local thread cache. It reduces the risk of memory consistency error. Any write to volatile variable in Java establishes a happen before the relationship with successive reads of that same variable.

What happens when a variable is marked as volatile?

When we declare a variable volatile, it means that all reads and all writes to this variable or from this variable will go directly into the main memory. The values of these variables will never be cached.


1 Answers

No, you will never get a NPE. This is because volatile also has the memory-effect of introducing a happens-before relationship. In other words, it will prevent reordering of

a = one; b = two; 

The statements above, will not be re-ordered, and all threads will observe value one for a if b already has value two.

Here is a thread in which David Holmes explains this:
http://markmail.org/message/j7omtqqh6ypwshfv#query:+page:1+mid:34dnnukruu23ywzy+state:results

EDIT (response to the follow-up): What Holmes is saying is, the compiler could in theory do a reorder if there were only thread A. However, there ARE other threads, and they CAN detect the reordering. That is why the compiler is NOT allowed to do that reordering. The java memory model requires the compiler specifically to make sure that no thread will ever detect such reordering.

But what if one is calling doIt() from another thread than the one calling setBothNonNull(...) ?

No, you will still NEVER have a NPE. volatile semantics do impose inter-thread ordering. Meaning that, for all existing thread, assignment of one happens before the assignment of two.

like image 64
Enno Shioji Avatar answered Sep 28 '22 00:09

Enno Shioji