Ruba Mushtaq
Ruba Mushtaq

Reputation: 411

How a thread can see stale reference of safely initialized object

I have been trying to figure out that how immutable objects which are safely published could be observed with stale reference.

public final class Helper {
private final int n;

  public Helper(int n) {
    this.n = n;
  }
} 

class Foo {
  private Helper helper;

  public Helper getHelper() {
    return helper;
  }

  public void setHelper(int num) {
    helper = new Helper(num);
  }
} 

So far I could understand that Helper is immutable and can be safely published. A reading thread either reads null or fully initialized Helper object as it won't be available until fully constructed. The solution is to put volatile in Foo class which I don't understand.

Upvotes: 1

Views: 232

Answers (3)

Andy Turner
Andy Turner

Reputation: 140494

The fact that you are publishing a reference to an immutable object is irrelevant here.

If you are reading the value of a reference from multiple threads, you need to ensure that the write happens before a read if you care about all threads using the most up-to-date value.

Happens before is a precisely-defined term in the language spec, specifically the part about the Java Memory Model, which allows threads to make optimisations for example by not always updating things in main memory (which is slow), instead holding them in their local cache (which is much faster, but can lead to threads holding different values for the "same" variable). Happens-before is a relation that helps you to reason about how multiple threads interact when using these optimisations.

Unless you actually create a happens-before relationship, there is no guarantee that you will see the most recent value. In the code you have shown, there is no such relationship between writes and reads of helper, so your threads are not guaranteed to see "new" values of helper. They might, but they likely won't.

The easiest way to make sure that the write happens before the read would be to make the helper member variable final: the writes to values of final fields are guaranteed to happen before the end of the constructor, so all threads always see the correct value of the field (provided this wasn't leaked in the constructor).

Making it final isn't an option here, apparently, because you have a setter. So you have to employ some other mechanism.

Taking the code at face value, the simplest option would be to use a (final) AtomicInteger instead of the Helper class: writes to AtomicInteger are guaranteed to happen before subsequent reads. But I guess your actual helper class is probably more complicated.

So, you have to create that happens-before relationship yourself. Three mechanisms for this are:

  • Using AtomicReference<Helper>: this has similar semantics to AtomicInteger, but allows you to store a reference-typed value. (Thanks for pointing this out, @Thilo).
  • Making the field volatile: this guarantees visibility of the most recently-written value, because it causes writes to flush to main memory (as opposed to reading from a thread's cache), and reads to read from main memory. It effectively stops the JVM making this particular optimization.
  • Accessing the field in a synchronized block. The easiest thing to do would be to make the getter and setter methods synchronized. Significantly, you should not synchronize on helper, since this field is being changed.

Upvotes: 3

Prashant Pandey
Prashant Pandey

Reputation: 4642

The variable helper is being read by multiple threads simultaneously. At the least, you have to make it volatile or the compiler will begin caching it in registers local to threads and any updates to the variable may not reflect in the main memory. Using volatile, when a thread starts reading a shared variable, it will clear its cache and fetch a fresh value from the global memory. When it finishes reading it, it will flush the contents of its cache into the main memory so that other threads may get the updated value.

Upvotes: 0

Jens Dibbern
Jens Dibbern

Reputation: 1454

Cite from Volatile vs Static in Java

This means that if two threads update a variable of the same Object concurrently, and the variable is not declared volatile, there could be a case in which one of the thread has in cache an old value.

Given your code, the following can happen:

  • Thread 1 calls getHelper() and gets null
  • Thread 2 calls getHelper() and gets null
  • Thread 1 calls setHelper(42)
  • Thread 2 calls setHelper(24)

And in this case your trouble starts regarding which Helper object will be used in which thread. The keyword volatile will at least solve the caching problem.

Upvotes: 1

Related Questions