During a data race can a thread ever read initial null value of volatile variable? especially when a non null value is assigned to it in constructor?

StackOverflow https://stackoverflow.com/questions/4343234

Question

  • What puzzles me is this.

Java doc of HashEntry in ConcurrentHashMap (jdk1.6.0_16)

...Because the value field is volatile, not final, it is legal wrt the Java Memory Model for an unsynchronized reader to see null instead of initial value when read via a data race. Although a reordering leading to this is not likely to ever actually occur, the Segment.readValueUnderLock method is used as a backup in case a null (pre-initialized) value is ever seen in an unsynchronized access method.

  • here is the implementation of get method of ConcurrentHashMap#Segment

    
    V get(Object key, int hash) {
            if (count != 0) { // read-volatile
                HashEntry e = getFirst(hash);
                while (e != null) {
                    if (e.hash == hash && key.equals(e.key)) {
                        V v = e.value;
                        if (v != null)
                            return v;
                        return readValueUnderLock(e); // recheck
                    }
                    e = e.next;
                }
            }
            return null;
        }
    
  • And of readValueUnderLock


V readValueUnderLock(HashEntry e) {
        lock();
        try {
            return e.value;
        } finally {
            unlock();
        }
    }
  • Based of my reading and understanding every thread will read an up to date value of volatile variable.

  • So when will a thread read initial null value? especially in HashEntry where value is assigned before the constructor completes. (Also note that HashEntry's reference never escapes its constructor.)

  • I am stumped, can some one explain the above java doc of HashEntry in ConcurrentHashMap (jdk1.6.0_16). and why that extra precaution locking is required?

Was it helpful?

Solution

When Java 1.5 was released, there was a provision in the JMM that said that the HashEntry can be partially initialized. That is, when a thread is putting into the map, the HashEntry is created and assigned as a reference to either the bucket head or a collison member. At that time the value of the entry, may have not been assigned to be seen by the other threads.

The CHM assumes that if the entry isnt null, then the value shouldn't be null so readValueUnderLock was put in as a failsafe.

I asked DL about this exact situation and he said that despite the possibility of it happening, it never should. He also said that since 1.6, that issue won't happen.

OTHER TIPS

You must make sure that the reference to the Map cannot be used by anyone, before the constructor completes. That should be easy to do when it is a private field and only accesses by getter methods - including in the class where that field is held.

The constructor will finish before the instance getter method will be able to be called.

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