Publishing and reading of non-volatile field
Asked Answered
C

7

13
public class Factory {
    private Singleton instance;
    public Singleton getInstance() {
        Singleton res = instance;
        if (res == null) {
            synchronized (this) {
                res = instance;
                if (res == null) {
                    res = new Singleton();
                    instance = res;
                }
            }
        }
        return res;
    }
}

It is almost correct implementation of thread-safe Singleton. The only problem I see is:

The thread #1 that is initializing the instance field can published before it will be initialized completely. Now, the second thread can read instance in a inconsistent state.

But, for my eye it is only problem here. Is it only problem here? (And we can make instance volatile).

Curitiba answered 4/12, 2017 at 17:4 Comment(2)
You should google "Double Checked Locking Java" - it is not a safe pattern to use for a singleton.Oza
Please note that I know it. My question is another.Curitiba
B
6

You example is explained by Shipilev in Safe Publication and Safe Initialization in Java. I highly recommend to read the whole article, but to sum it up look at UnsafeLocalDCLFactory section there:

public class UnsafeLocalDCLFactory implements Factory {
  private Singleton instance; // deliberately non-volatile

  @Override
  public Singleton getInstance() {
    Singleton res = instance;
    if (res == null) {
      synchronized (this) {
        res = instance;
        if (res == null) {
           res = new Singleton();
           instance = res;
        }
      }
    }
    return res;
  }
}

Above has following problems:

The introduction of local variable here is a correctness fix, but only partial: there still no happens-before between publishing the Singleton instance, and reading of any of its fields. We are only protecting ourselves from returning "null" instead of Singleton instance. The same trick can also be regarded as a performance optimization for SafeDCLFactory, i.e. doing only a single volatile read, yielding:

Shipilev suggests to fix as follows, by marking instance volatile:

public class SafeLocalDCLFactory implements Factory {
  private volatile Singleton instance;

  @Override
  public Singleton getInstance() {
    Singleton res = instance;
    if (res == null) {
      synchronized (this) {
        res = instance;
        if (res == null) {
          res = new Singleton();
          instance = res;
        }
      }
    }
    return res;
  }
}

There are no other problems with this example.

Birdbath answered 19/5, 2018 at 14:9 Comment(6)
"There are no other problems with this example.". Ok, why? Could you point how that claim is founded in JMM?Curitiba
@KarolDowbecki while I know about that article(s) - I re-read them once in a few months, truly understanding them is not that easy and Gilgamesz has a very good point here. I highly doubt that besides a few users on SO, someone can really explain this and by really I mean in plain english and it would probably take a lot of pages tooInhumation
@Curitiba it is absolutely wrong to think about this way, but easy to understand, writing a volatile and then reading it, will introduce the necessary memory barriers so that all fields of Singleton are written before they are read, which I tried explaining to a question of yours here #45152263Inhumation
@Curitiba also notice that this is very think ice to walk on - even if I think I have the basic knowledge about these things, I absolutely never write code like this in production - I stick to the known patterns that Shipilev has, again, there are only a handful users here on SO that truly understand these things... one of them is Shipilev himselfInhumation
I have read this answer by accident again after more then two years and now I am more convinced that this answer does not address the question and I agree with @Curitiba on that part : ""There are no other problems with this example.". Ok, why?". You took what Shipilev said out of context really. The single volatile read is supposed to have the racy part in it, otherwise in SafeLocalDCLFactory there is no single volatile read, but there is a single RACY volatile read. The question from OP goes on to mention "consistency" of the Singleton - he really means what would happen ifInhumation
Singelton would have fields? How would that work?Inhumation
I
3

EDIT I've written one more answer here that should clear all the confusion.

This is a good question, and I'll try to summarize my understanding here.

Suppose Thread1 is currently initializing Singleton instance and publishes the reference (unsafely obviously). Thread2 can see this un-safe published reference (meaning it sees a non-null reference), but that does not mean that the fields that it sees via that reference (Singleton fields that are initialized via the constructor) are initialized correctly too.

As far as I can see, this happens because there could be re-ordering of the stores of the fields happening inside the constructor. Since there is no "happens-before" rules (these are plain variables), this could be entirely possible.

But that is not the only problem here. Notice that you do two reads here:

if (res == null) { // read 1

return res // read 2

These reads have no synchronization protection, thus these are racy reads. AFAIK this means that read 1 is allowed to read a non-null reference, while read 2 is allowed to read a null reference.

This btw is the same thing that the ALL mighty Shipilev explains (even if I read this article once 1/2 year I still find something new every time).

Indeed making instance volatile would fix things. When you make it volatile, this happens:

 instance = res; // volatile write, thus [LoadStore][StoreStore] barriers

All "other" actions (stores from within the constructor) can not pass this fence, there will be no re-orderings. It also means that when you read the volatile variable and see a non-null value, it means that every "write" that was done before writing the volatile itself has occurred for sure. This excellent post has the exact meaning of it

This also solves the second problem, since these operations can not be re-ordered, you are guaranteed to see the same value from read 1 and read 2.

No matter how much I read and try to understand these things are constantly complicated to me, there are very few people that I know that can write code like this and reason correctly about it too. When you can (I do!) please stick to the known and working examples of double check locking :)

Inhumation answered 6/12, 2017 at 12:59 Comment(5)
"These reads have no synchronization protection, thus these are racy reads. AFAIK this means that read 1 is allowed to read a non-null reference, while read 2 is allowed to read a null reference." There is no race because there is only one read of instance.Curitiba
Please note that there is data dependency between: Singleton res = instance and return res. So, the first statement must be executed fristly. Noone (CPU, compiler, interpreter and so on) cannot do reordering because it guaranteed by every (nearly) CPU / memory model. Otherwise, every our program will be broken.Curitiba
You are saying "please stick to the known and working examples of double check locking". No it doesn't always work see shipilev.net/blog/2014/safe-public-construction, look for "Double-Checked Locking idiom".Birdbath
@Curitiba regarding your first comment - of course there are two reads, return res is reading res before returning it.Inhumation
@Curitiba regarding your second comment, well, if only you could understand russian... youtube.com/watch?v=C6b_dFtujKo&t=2111sInhumation
N
3

Normally I would never use a double checked locking mechanism anymore. To create a thread safe singleton you should let the compiler do this:

public class Factory {
    private static Singleton instance = new Singleton();
    public static Singleton getInstance() {
        return res;
    }
}

Now you are talking to make the instance volatile. I don't think this is necessary with this solution as the jit compiler now handlers the synchronization of the threads when the object is constructed. But if you want to make it volatile, you can.

Finally I would make the getInstance() and the instance static. Then you can reference Factory.getInstance() directly without constructing the Factory class. Also: you will get the same instance across all threads in your application. Else every new Factory() will give you a new instance.

You can also look at Wikipedia. They have a clean solution if you need a lazy solution:

https://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java

// Correct lazy initialization in Java
class Foo {
    private static class HelperHolder {
       public static final Helper helper = new Helper();
    }

    public static Helper getHelper() {
        return HelperHolder.helper;
    }
}
Netta answered 22/5, 2018 at 6:41 Comment(3)
You don't answer the question. Please note that your Singleton is the simplest: it is not lazy.Curitiba
I did not read an article completely, but please note that an article is very old. I suppose that the article is just obsolete.Curitiba
I removed the article and added a different solution, found on wikipedia.Netta
P
2

I do that like this:

public class Factory {
    private static Factory factor;
    public static Factory getInstance() {
        return factor==null ? factor = new Factory() : factor;
    }
}

Just simply

Plataea answered 25/5, 2018 at 8:27 Comment(1)
this is only good when Factory does not depend on any other properties, that are not "static"; but even so in such a case a simple static field or a enum singlenton is betterInhumation
I
1

After some time (yeah it took 2 years, I know), I think I have the proper answer. To take it literally, the answer to this:

But, for my eye it is only problem here. Is it only problem here?

Would be yes. The way you have it right now, callers of getInstance will never see a null. But if Singleton would have fields, there is no guarantee that those fields will be correctly initialized.

Let's take this slow, since the example is beautiful, IMHO. That code you showed does a single (racy) volatile read :

public class Factory {
    private Singleton instance;
    public Singleton getInstance() {
        Singleton res = instance;      // <-- volatile RACY read
        if (res == null) {
            synchronized (this) {
                res = instance;        // <-- volatile read under a lock, thus NOT racy
                if (res == null) {
                    res = new Singleton();
                    instance = res;
                }
            }
        }
        return res;
    }
}

Usually, the classical "double check locking" has two racy reads of volatile, for example:

public class SafeDCLFactory {

   private volatile Singleton instance;

   public Singleton get() {
     if (instance == null) {             // <-- RACY read 1 
       synchronized(this) {
         if (instance == null) {         // <-- non-racy read
            instance = new Singleton();
         }
       }
     }
     return instance;                    // <-- RACY read 2
   }
} 

Because those two reads are racy, without volatile, this pattern is broken. You can read how we can break here, for example.

In your case, there is an optimization, that does one less reading of a volatile field. On some platforms this matters, afaik.

The other part of the question is more interesting. What if Singleton has some fields that we need to set?

static class Singleton {
     
   //setter and getter also
   private Object obj;  
   
} 

And a factory, where Singleton is volatile:

static class Factory {

    private volatile Singleton instance;

    public Singleton get(Object obj) {
        if (instance == null) {
            synchronized (this) {
                if (instance == null) {
                    instance = new Singleton();
                    instance.setObj(obj);
                }
            }
        }
        return instance;
    }
}

We have a volatile field, we are safe, right? Wrong. The assign of obj happens after the volatile write, as such there are no guarantees about it. In plain english: this should help you a lot.

The correct way to fix this is to do the volatile write with an already build instance (fully build):

  if (instance == null) {
       Singleton local = new Singleton();
       local.setObj(obj);
       instance = local;
  }
Inhumation answered 11/7, 2020 at 3:49 Comment(0)
J
0

Now, the second thread can read instance in a inconsistent state.

I'm pretty sure that really is the only issue in that code. The way I understand it, as soon as the line

instance = res;

is executed, another thread could read instance and see it as non-null, and thus skips the synchronized. This means there is no happens-before relation between those two threads, because those only exist if both threads synchronize on the same object or access the same volatile fields.

The other answers already linked to Safe Publication and Safe Initialization in Java, which offers the following ways to solve the unsafe publication:

  • Making the instance field volatile. All threads have to read the same volatile variable, which establishes a happens-before relation

    A write to a volatile field (§8.3.1.4) happens-before every subsequent read of that field.

  • Wrapping the singleton into a wrapper which stores the singleton in a final field. The rules for final fields are not as formally specified as the happens-before relations, the best explanation I could find is in final Field Semantics

    An object is considered to be completely initialized when its constructor finishes. A thread that can only see a reference to an object after that object has been completely initialized is guaranteed to see the correctly initialized values for that object's final fields.

    (Not the emphasis and restriction to the final fields, other fields might be seen in inconsistent state at least theoretically)

  • Making sure the singleton itself only contains final fields. The explanation would be the same as the one above.

Jumbo answered 25/5, 2018 at 15:54 Comment(0)
A
0

The problem with the code mentioned in the question is that reordering can happen and a thread can get a partially constructed object of the singleton class.

When I say reordering, I mean the following:

public static Singleton getInstance() {
    if (instance == null) {
        synchronized (Singleton.class) {
            if (instance == null) {
                instance = new Singleton();
                /* The above line executes the following steps:
                   1) memory allocation for Singleton class
                   2) constructor call ( it may have gone for some I/O like reading property file etc...
                   3) assignment ( look ahead shows it depends only on memory allocation which has already happened in 1st step. 
                   If compiler changes the order, it might assign the memory allocated to the instance variable. 
                   What may happen is half initialized object will be returned to a different thread )
                */
            }
        }
    }
    return instance;
} 

Declaring the instance variable volatile ensures a happens-before/ordered relationship on the above mentioned 3 steps:

A write to a volatile field (§8.3.1.4) happens-before every subsequent read of that field.


From Wikipedia's Double-checked locking:

As of J2SE 5.0, this problem has been fixed. The volatile keyword now ensures that multiple threads handle the singleton instance correctly. This new idiom is described in The "Double-Checked Locking is Broken" Declaration:

// Works with acquire/release semantics for volatile
// Broken under Java 1.4 and earlier semantics for volatile
class Foo {
    private volatile Helper helper = null;
    public Helper getHelper() {
        Helper result = helper;
        if (result == null) {
            synchronized(this) {
                result = helper;
                if (result == null) {
                    helper = result = new Helper();
                }
            }
        }
        return result;
    }

    // other functions and members...
}
Admonish answered 26/5, 2018 at 14:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.