The below example is from the book "Java Concurrency in Practice" by Brian Goetz, Chapter 3, Section 3.5.1. This is an example of Improper publication of objects:
class SomeClass {
public Holder holder;
public void initialize() {
holder = new Holder(42);
}
}
public class Holder {
private int n;
public Holder(int n) { this.n = n; }
public void assertSanity() {
if (n != n)
throw new AssertionError("This statement is false");
}
}
It says that the Holder could appear to another thread in an inconsistent state and another thread could observe a partially constructed object. How can this happen? Could you give a scenario using the above example?
Also it goes on to say that there are cases when a thread may see a stale value the first time it reads a field and then a more up to date value the next time, which is why the assertSanity
can throw AssertionError
. How can the AssertionError
be thrown?
From further reading, one way to fix this problem is to make Holder
immutable by making the variable n
final. For now, let us assume that Holder
is not immutable but effectively immutable.
To safely publish this object, do we have to make holder initialization static and declare it as volatile (both static initialization and volatile or just volatile)?
Something like this:
public class SomeClass {
public static volatile Holder holder = new Holder(42);
}
n
is not public and cannot be viewed outside of the class. – Olibanumholder
is public, and another thread may callassertSanity
on it before its constructor and field initialisation have completed - https://mcmap.net/q/357597/-why-is-double-checked-locking-broken-in-java. And maybe - https://mcmap.net/q/357598/-java-concurrency-in-practice-sample-14-12. – Trilbeeholder
until after the constructor has returned (if the JITC is compliant). – Olibanumholder
being visible. You can look at ConcurrentHashMap's private methodreadUnderLock
in which that takes this nuance into account. Though declaringholder
as volatile does. – Hsining