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.