Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Singleton without volatile member

Let's say I have next class:

public class Singleton{

  private static Singleton _instance; 

  public static Singleton getInstance(){

   if(_instance == null){
         synchronized(Singleton.class){
           if(_instance == null)
           _instance = new Singleton();
         }
   }
   return _instance;
 }

For example we have two threads A and B which try to execute getInstance() method simultaneously. Am I understand the process correctly:


  1. Thread A enter into getInstance() method and acquire the lock;
  2. Thread B also enter into getInstance() method and blocked;
  3. Thread A create new Singleton() object and release the lock, now last value of _instance variable should be visible to thread B? Or thread B still could have own copy of _instance variable which is not synchronized with the main memory(_instance=null)?
like image 257
Iurii Avatar asked Jan 08 '23 10:01

Iurii


1 Answers

Thread B is blocked on the synchronize, when it proceeds it will see the changed field _instance != null and therefore does not construct one but return the existing.

All other threads which come later see the instance being set and will not even lock.

Problem: your code is incomplete, you need volatile in order to make sure threads which do not go through the synchronized (most of them, hopefully) still only see a completely published singleton object.

The Java Memory Model does only guarantee that the final fields are initialized. For all others you need a safe publish, which is possible with:

  • Exchange the reference through a properly locked field (JLS 17.4.5)
  • Use static initializer to do the initializing stores (JLS 12.4)
  • Exchange the reference via a volatile field (JLS 17.4.5), or as the consequence of this rule, via the AtomicX classes
  • Initialize the value into a final field (JLS 17.5).

The easiest method to avoid the volatile (or an atomic reference which is also safe to publish objects to other threads) is to use normal Object initialisation, this is a valid and robust singleton (but not lazy) provided by the JVM:

class Singleton
{
    private static final Singleton HIGHLANDER = new Singleton();
    private Singleton() { } // not accessible
    public static getSingleton() { return HIGHLANDER; }
}

JDK internally uses this similar construct with "Holder" objects to implement the same simple and robust pattern but in a lazy fashion:

class Singleton
{
    private Singleton() { } // not accessible
    private static Class LazyHolder {
        private static final Singleton LAZY_HIGHLANDER = new Singleton();
    }
    public static Singleton getInstance() {
        return LazyHolder.LAZY_HIGHLANDER;
    }
}

Both methods do not require volatile variable access (which you need in DCL case) or synchronisation (it is implicitly done by the JVM which does the initialisation protected by a class lock).

like image 113
eckes Avatar answered Jan 10 '23 22:01

eckes