If you assign an object to a destination field, will other threads see previous updates to this odd / non-volatile object fields?

While reading Java language specifications, I found this excerpt about finite fields:

The usage model for final fields is simple: set the final fields for an object in this object constructor; and do not write a link to an object that is being built in a place where the thread can see it until the constructor of the object is completed. If this is, then when the object is being viewed by another thread, this thread will always see a correctly constructed version of this object's finite fields. He will also see the versions of any object or array referenced by those trailing fields, which are at least the last since trailing fields .

Link: https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.5

My question is, does version mean updates? Does this mean that after the construction is completed, non-final / non-volatile fields of the object referenced by the final field will also be read from the main memory (and not the local cache)?


Example

So, let's say thread #1 creates objectB and sets one of its non-financial / non-volatile fields.

Then thread #2 sets the same field to something else, creates another objectA with the final field specified as objectB , then puts objectA somewhere where thread #1 can get it.

thread #1 then gets objectA and sees its final field as objectB . Is it possible for thread #1 not to see the changes to objectB made by thread #2 ?

Or here is what code shows what I mean:

 public class Test { private static final ConcurrentLinkedQueue<A> myAs = new ConcurrentLinkedQueue<>(); private static long timer = System.nanoTime() + 3000000000L; // 3 seconds into the future public static void main(String... args) { B myB = new B("thread #1"); // Set in thread 1 new Thread(() -> { myB.setString("thread #2"); // Set in thread 2 myAs.add(new A(myB)); }).start(); for(long i = 0; i < x; i = System.nanoTime()) {} // Busy-wait for about 3 seconds System.out.println(myAs.poll().getB().getString()); // Print out value } public static class A { private final B b; public A(B b) { this.b = b; } public B getB() { return b; } } public static class B { private String s = null; public B(String s) { this.s = s; } public String getString() { return s; } public void setString(String s) { this.s = s; } } } 

It seems that the code is reading the updated values, but I'm not sure if this is just by accident.

+7
java multithreading concurrency thread-safety volatile
source share
3 answers

IMHO, you will always see the updated value. There are two things here.

  • Secure publishing
  • Commit action at the end of the constructor

Since we have a freeze action, another thread should be able to see the contents of myB

More details: https://shipilev.net/blog/2014/jmm-pragmatics/#_part_v_finals

https://www.ibm.com/developerworks/library/j-jtp03304/index.html

+1
source share

"Is it possible for thread #1 not to see the changes to objectB made by thread #2 ?"

Yes. Since thread #1 can cache the value.

A quote from spec means what happened before between assigning the final value of the field and publishing the object.

+1
source share

This is interesting, and I think I really read about it a while ago ... Here is an example (if I remember the example correctly):

  static class Holder { private final String[] names; static Holder newHolder; public Holder() { super(); names = new String[3]; names[0] = "first"; names[1] = "last"; } public void newObject() { newHolder = new Holder(); newHolder.names[2] = "oneMore"; } public void readObject() { System.out.println(Arrays.toString(newHolder.names)); } } 

Suppose there are two threads here: ThreadA and ThreadB . Now suppose ThreadA calls newObject ; when he did ThreadB calls readObject .

There is no guarantee that ThreadB print first, last, oneMore ; it is only guaranteed that first and last will be present accurately.

This battle is understandable if you think in terms of MemoryBarriers , which are used in the case of final fields used inside constructor .

In the current implementation, it looks something like this:

 public Holder() { super(); names = new String[3]; names[0] = "first"; names[1] = "last"; } // [StoreStore] // [LoadStore] 

There are two barriers that are inserted at the end of the constructor that prevent other reads and storages.

+1
source share

All Articles