I read several related questions, but none of them explains ways of safe publication of the Holder. I am still confused about example from Java Concurrency in Practice, section 3.5:
There is the class Holder:
public Holder {
private int n;
public Holder(int n) { this.n = n };
public void assertSanity() {
if(n != n)
throw new AssertionError("This statement is false.");
}
}
and its unsafe publication:
//unsafe publication
public Holder holder;
public void initialize() {
holder = new Holder(42);
}
The AssertionError could be thrown and I agree. The authors write that it is because of unsafe publication, but on the other hand there is no answer: what would be the proper way of publication? They indicates 4 safe publication idioms, but I do not understand, why would they work in the above case:
To publish an object safely, both the reference to the object and the object's state must be made visible to other threads at the same time. A properly constructed object can be safely published by:
- Initializing an object reference from a static initializer;
- Storing a reference to it into a volatile field or AtomicReference;
- Storing a reference to it into a final field of a properly constructed object;
- or Storing a reference to it into a field that is properly guarded by a lock.
I agree with 1&4, but have doubts why following publications would work:
//safe publication
public volatile Holder holder;
or
//safe publication
public final Holder holder;
volatile & final have impact only for the reference, not for the referenced object state, so I think the AssertionError would be still possible, right?
Instead of publication refinement, Authors show how to make the Holder immune for the unsafe publication, by:
private final int n;
I am curious if the following would also work? How is it connected with (effective) immutability?
private volatile int n;
It is my first question here, thank you for your help!