Singleton Design Pattern in Java

  Pi Ke        2020-04-11 02:16:28       14,992        2    

Singleton is frequently used in applications where resource may be expensive to create and no instance specific state needs to be maintained. For example, when creating database connection, a singleton may be needed. Today we will share the famous Singleton design pattern in Java.

1. Definition

Singleton design pattern is a design pattern that restricts the instantiation of a class to one object. It is one of the most well-known design patterns.

2. Application

Singleton can be used in many occasions, for example, one database can have only one connection or connection counter of a website.

3. Some implementations

The basic form is that using private constructor and a public static method to get the object instance. 

Snippet 1:

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

Since constructor is private, we cannot call new Singleton() to create a new instance, instead we can only call Singleton.getInstance() to obtain the instance. Because this instance is static, there is only one instance can exist for the class, no matter how many times Singleton.getInstance() is invoked.

The disadvantage of snippet 1 is that the instance will be created when this class is loaded. This increases the performance overhead. Lazy initialization is frequently used to create the instance when needed.

Snippet 2:

public class Singleton {
    private static Singleton instance = null;
    private Singleton(){}
    public static Singleton getInstance() {
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

For snippet 2, it's fine if the instance is only used in a single thread environment. But in multithreading environment, two or more threads may invoke Singleton.getInstance() at the same time, there may be two or more instances created. Hence synchronization is needed to guarantee only one instance wil be created.

Snippet 3:

public class Singleton {
    private static Singleton instance = null;
    private Singleton(){}
    public static synchronized  Singleton getInstance() {
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

In snippet 3, synchronized is applied to the getInstance() method, only one thread is allowed to execute the getInstance() method.  Once the object created, the instance=new Singleton() will not be executed anymore, however, it's not very efficient to synchronize the whole method. To be efficient, Double-checked locking method can be adopted.

Snippet 4:

public class Singleton {
    private static Singleton instance = null;
    private Singleton(){}
    public static Singleton getInstance() {
        if(instance == null){
            synchronized(Singleton.class){
                if(instance == null)
                    instance = new Singleton();
            }
        }
        return instance;
    }
}

Now below block is synchronized.

if(instance == null)
   instance = new Singleton();

If an object is created, then the codes enclosed in the first if statement will not be executed. It will be synchronized only when the first time the object is being created. Later multiple threads can execute the getInstance() method without any blocking.

Unfortunately, there is an out-of-order write problem, for more details , please read : The "Double-Checked Locking is Broken" Declaration, Double-checked locking and the Singleton pattern.

The execution order for executing instance=new Singleton() should be :

  1. Allocate memory
  2. Constructor initialization
  3. Assign object reference to instance.

Because of Java Memory Model problem, we may encounter the out-of-order write problem:

  1. Allocate memory
  2. Assign reference to instance
  3. Constructor initialization

The object is not initialized, but instance!=null is true. Now if another thread operates on the instance, some unexpected results may happen.

As of J2SE 5.0 and later, the above problem is fixed. A volatile keyword can be added to ensure that multiple threads handle the singleton instance correctly.

Snippet 5:

public class Singleton {
    private static volatile Singleton instance = null;
    private Singleton(){}
    public static Singleton getInstance() {
        if(instance == null){
            synchronized(Singleton.class){
                if(instance == null)
                    instance = new Singleton();
            }
        }
        return instance;
    }
}

To achieve lazy loading to save resource and also at the same time to provide thread safety, another way can be adopted which utilizes the inner static class declaration capability.

Snippet 6:

public class Singleton {
  private static class SingletonHolder {
    private static final Singleton singleton = new Singleton();
    private SingletonHolder() {
    }
  }

  private Singleton() {
  }

  public static Singleton getInstance() {
    return SingletonHolder.singleton;
  }
}

This is indeed a recommended way to implement the singleton pattern.

Since Java 5, enum is introduced and it is designed to provide access to just one instance of any element defined.

Snippet 7:

public enum Singleton {
    INSTANCE;
    //Methods below
}

For more details about enum and Singleton, you can check out Josh Bloch's Effective Java and also here is an video on Google I/O.

4. Limitation

  1. Because of private constructor, Singleton cannot be inherited
  2. If an app is running in a container, we should be careful because servlet may be loaded by several classloader, so there may be a few Singleton instances existed
  3. If a Singleton is serializable, if serialize the instance once and then deserialize a few times, there may be a few Singleton instances
  4. All above methods may suffer the attack of Reflection API. i.e, when using Java Reflection, the private constructor can be accessed and new instance can be created by calling the private constructor.

5. Conclusion

Sometimes even a simple design pattern can have many variations, so be careful about traps in a design pattern. If you know where the trap is, you can avoid it easily.

JAVA  MULTITHREAD  SINGLETON  DESIGN PATTERN 

       

  RELATED


  2 COMMENTS


AnyMore [Reply]@ 2016-05-08 05:53:13
public enum Singleton {
    INSTANCE;
}

================================
in jvm 

public static final Type INSTANCE;
private static final Type a[];

staic{
  INSTANCE=new Type("INSTANCE",0);
  a=new Type[]{INSTANCE};
}

so Snippet 6==Snippet 1;
Ke Pi [Reply]@ 2016-05-09 02:49:56

You are right. 



  RANDOM FUN

Lose some weight