Search code examples
javaconcurrencydouble-checked-locking

Double-Check-Locking guarantees state of the object ? (concurrency in practice)


I am reading concurrency in practice and have some misunderstanding.
quote:

the real problem with DCL is the assumption that the worst thing that can happen when reading the shared object reference without synchronization is to erroneously see a stale value (in this case, null); in that cse the DCL idiom compensates for this risk by trying again with the lock held. But the worst case is actually considerably wrong - it is possible to see a current value of the reference but stale values for the object's states, meaning that the object could be seen to be in an invalid or incorrect state.

after Brian Goetz writes that DCL will work in current memory model using volatile:

public class DoubleCheckLociing{

   private static volatile Resource resource;

   public static Resource getInstance(){
       if(resource == null){
           synchronized(DoubleCheckLociing.class){
               if(resource == null){
                   resource = new Resource();
               }
            }
       } 
       return resource;
   }
}

I am not sure if understand the phrase about state correct.

Lets imagine that Resource class looks like this:

class Resource{
    private Date date = new Date();//mutable thread unsafe class
    private int k = 10;

    public Date getDate(){
        return date;
    }

   public int getK(){
        return k;
    }

}

Does I have guaranties that getInstance always return correct resource which always return correct k (10) and date?


Solution

  • With the volatile in place you do have those guarantees. Without the volatile you do not.

    When one thread writes the volatile variable resource, the operation includes a 'memory barrier' that ensures that everything it wrote before that, like the initialization of the instance fields, is written out to system memory first.

    When another thread reads resource, it includes a memory barrier that ensures that no reads performed after that will see values that were cached from system memory before that read.

    These two memory barriers ensure that if a thread sees an initialized resource variable, then it will also see properly initialized fields in that object.