The behaviour of making a non-volatile reference to a volatile object in Java

Coming from C/C++, I am a little confused about volatile object behavior in Java.

I understand that volatile in Java has two properties:

  1. Won't bring object into cache, always keep it in main memory.
  2. Guarantee "happen-before"

However I am not sure what happens if I make a new non-volatile reference to object. For example,

class Example {
   private volatile Book b = null;

   public init() { b = new Book(...); }

   public use() {
     Book local = b;
     local.read();
   }
}

AFAIK, volatile means the "book object" that b is referencing to should be in main memory. Compiler probably implement reference as pointer internally, so the b pointer probably sit in cache. And to my understanding, volatile is a qualifier for object, not for reference/pointer.

The question is: in the use method, the local reference is not volatile. Would this "local" reference bring the underlying Book object from main memory into cache, essentially making the object not "volatile"?

2 answers

  • answered 2018-01-19 08:14 JB Nizet

    volatile is about the reference, not the object.

    It guarantees that any thread reading the variable b after another thread has set the value of b will get the value assigned to b by the other thread, and not some cached value.

  • answered 2018-01-19 10:44 Holger

    There is no such thing as a “volatile object” nor “always keep it in main memory” guarantees.

    All that volatile variables of a reference type guaranty, is that there will be a happens-before relationship between a write to that variable and a subsequent read of the same variable.

    Since happens-before relationships are transitive, they work for your example code, i.e. for b = new Book(...) all modifications made to the Book instance are committed before the reference is written to b and hence for Book local = b; local.read(); the read() is guaranteed to see all these modification made by the other thread before writing the reference.

    This does not imply that the Book instance’s memory was special. E.g. modifications made to the instance after the reference has been written to b may or may not be visible to other threads and other threads may perceive only some of them or see them as if being made in a different order.

    So it doesn’t matter which way you get the reference to the object, all that matters is whether the changes are made before or after publishing a reference to that object through b. Likewise, it doesn’t matter how you perform the read access to the object, as long as you do it after having acquired the reference by reading b.

    With local.read(); you are accessing the object via the local variable local and within read(), the same reference will be accessed though this, but all that matters is that you have acquired the reference by reading b before reading the object’s state.