Question

I have two classes A and B:

class A {
    private final String someData;
    private B b;

    public String getSomeData() { return someData; }

    public B getB() {
        if (b == null) {
             b = new B(someData);
        }
        return b;
    }
}

where B is immutable and computes its data only from an instance of A. A has immutable semantics, but it's internals are mutable (like hashCode in java.lang.String).

When I call getB() from two different threads, and the calls overlap, I assume each thread gets its own instance of B. But since the constructor of B gets only immutable data, the two instances of B should be equal.

Is that correct? If not, must I make getB() synchronized to make it thread-safe?

Assume that B implements equals(), which compares all instance variables of B. Same for hashCode()

Was it helpful?

Solution

This is not thread-safe, because you haven't created any "happens-before" relationships with volatile or synchronized, so it's possible for the two threads to interfere with each other.

The problem is that although b = new B(someData) means "allocate enough memory for an instance of B, then create the instance there, then point b to it", the system is allowed to implement it as "allocate enough memory for an instance of B, then point b to it, then create the instance" (since, in a single-threaded app, that's equivalent). So in your code, where two threads can create separate instances but return the same instance, there's a chance that one thread will return the other thread's instance before the instance is fully initialized.

OTHER TIPS

For "But since the constructor of B gets only immutable data, the two instances of B should be equal." As you understand its not thread safe, one thread might get un-initialized B instance (B as null or inconsistent state where somedata not yet set) others might get b instance with somedata set.

To fix this you need synchronized getB method or use synchronized block with double-check lock or some non-blocking technique like AtomicReference. For you reference I am adding here sample code for how to achieve the correct threadSafe getB() method using AtomicReference.

class A {
    private final String someData = "somedata";
    private AtomicReference<B> bRef;

    public String getSomeData() { return someData; }

    public B getB() {
        if(bRef.get()== null){
            synchronized (this){
                if(bRef.get() == null)
                    bRef.compareAndSet(null,new B(someData));
            }
        }
        return bRef.get();
    }
}

class B{
    public B(String someData) {

    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top