define

Ensure that a class has only one instance and provide a global access point to access it.

Hangry singleton

Classes are instantiated when they are initialized

      public class Singleton {
          // Private static member variables
          private static Singleton instance = new Singleton();
          // Private constructor
          private Singleton(a){}
          Public static access methods
          public static Singleton getInstance(a)
          {
              returninstance; }}Copy the code

This way the initialization is done at class load time, so class load is slower, but object retrieval is faster.

Lazy singleton

It is instantiated the first time the instance is called

public class Singleton {
      // Private static member variables
      private static Singleton instance = null;
      // Private constructor
      private Singleton(a){}
      // Public static access methods, note that the synchronized keyword to ensure thread safety
      public static synchronized Singleton getInstance(a)
      {
          if(instance == null)
              instance = new Singleton();
          returninstance; }}Copy the code

This works well in multiple threads, but synchronization is required every time the getInstance method is called, incurring unnecessary synchronization overhead, and most of the time we don’t need synchronization, so it’s not recommended.

Double check lock

public class Singleton {
    private static Singleton sInstance;
    public static Singleton getInstance(a) {
        if (sInstance == null) {/ / position 1
            synchronized (Singleton.class) {
                if (sInstance == null) {
                    sInstance = newSingleton();/ / position 2}}}return sInstance;
    }
    private Singleton(a) {}}Copy the code

In multithreaded environments, getInstance() checks whether the singleton has been initialized. If it has been initialized, it returns the singleton directly. If it has not been initialized, it initializes the singleton in the synchronized code block and then returns it.

But this is a bad optimization, and the root of the problem is position 2

sInstance =new Singleton(); This statement creates an object that can be decomposed into three lines of code:

memory = allocate();// 1. Allocate the memory space of the objectctorInstance(memory);// 2. Initialize the objectsInstance = memory;// 3. Set sInstance to the newly allocated memory address
Copy the code

Reordering may occur between 2 and 3 in the pseudocode above, and the order of execution after reordering is as follows:

memory = allocate();// 1. Allocate the memory space of the objectsInstance = memory;// 2. Set sInstance to the newly allocated memory address before the object is initializedctorInstance(memory);// 3. Initialize the object
Copy the code

Because this reordering does not affect the specification in the Java specification: Intra-Thread Sematics allows reordering that does not change the results of a single-threaded program execution within a single thread.

But the following can happen when multiple threads are running concurrently:Thread B accesses an uninitialized object.

Solution 1:

public class Singleton {
private static volatile Singleton sInstance;
public static Singleton getInstance(a) {
        if (sInstance == null) {
                synchronized (Singleton.class) {
                        if (sInstance == null) {
                           sInstance = newSingleton(); }}}return sInstance;
            }
private Singleton(a) {}}Copy the code

When the object is specified as volatitle, reordering is disabled in multi-threaded environments

Solution 2:

public class Singleton {
    private Singleton(a){};
    private static class Inner{
        private static Singleton SINGLETION=new Singleton();
    }
    public static Singleton getInstance(a){
        returnInner.SINGLETION; }}Copy the code

A static Inner class is not initialized when the outer class is initialized. It is loaded and initialized separately. The Inner class is initialized when the getInstance method is first executed.

The static object SINGLETION is initialized during the Inner class initialization phase, which is when the virtual machine executes the class constructor < Clinit >() method.

The virtual machine ensures that a class’s < Clinit >() methods are locked and synchronized correctly in a multithreaded environment. If multiple threads initialize a class at the same time, only one thread will execute the class’s < Clinit >() methods, and all other threads will block and wait.

The enumeration

public enum Singleton {  
     INSTANCE;  
     public void doSomeThing(a) {}}Copy the code

The default creation of enumerated instances is thread-safe and singleton in any case. One of the implementations of the singleton pattern described above where they recreate the object is deserialization, where a singleton object is written to disk and then read back, thus obtaining an instance. The deserialization operation provides the readResolve method, which gives developers control over deserialization of objects. In the examples above, to prevent the deserialization of the singleton or to regenerate the object, the following methods must be added:

private Object readResolve(a) throws ObjectStreamException {
	return singleton;
}
Copy the code

Refer to the article

  • Seven ways to write a singleton pattern
  • Problems with traditional singleton double-checked locking