##Singleton mode (Java) (direct loading, delayed loading)

Posted by chyan on Mon, 07 Feb 2022 13:22:47 +0100

Singleton definition: ensure that the class has one and only one instance, and provide a global access point about it
Direct loading: globally unique and immutable from beginning to end
Load on demand: the singleton is empty before the external call. After the first external call, it is globally unique and immutable

1 direct loading

public class Singleton {
    //	Private: private, which ensures encapsulation. The external can only be accessed through the get method, not directly
    //	Static: static, which ensures that the member belongs to a class (not an object); If the static modifier is not written, it indicates that the member belongs to an object, which cannot be a singleton
    //	final: the member is immutable
    private static final Singleton INSTANCE = new Singleton();
    
    //	The private constructor cannot be called externally, that is, the class cannot be instantiated by calling the constructor
    private Singleton() {
        
    }
    
    //	Public static get method for external access to private instances
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

2.0 Load On Demand

public class Singleton {
    
    //	Without final, INSTANCE has not been instantiated
    private static Singleton INSTANCE = null;
    
    
    private Singleton() {
        
    }
    
    //	Re instantiation of external call
    public static Singleton getInstance() {
        if (INSTANCE == null) {
            //	Problem: in multithreaded environment, INSTANCE is easy to be instantiated many times
            INSTANCE = new Singleton();	// Statement 1
        }
        return INSTANCE; //	Statement 2
    }
}
Why is INSTANCE easy to be instantiated multiple times in a multithreaded environment?

For example, assuming that INSTANCE has not been instantiated, thread A and thread B access the getInstance() method at the same time,

Step 1: thread A executes statement 1. At this time, thread A obtains an INSTANCE of INSTANCE, which is set to x, that is, INSTANCE==x

Step 2: thread B executes statement 1. At this time, thread B obtains another new INSTANCE of INSTANCE, which is set to y, that is, INSTANCE==y

Step 3: thread A executes statement 2, where INSTANCE==y

Question:
  1. Thread A should return x instead of being replaced
  2. INSTANCE is a single INSTANCE and should be globally unique. It cannot be x first and then y later
  3. The more threads, the more serious the problem
solve:

Only one initial thread can execute new Singleton() to generate an instance, and all threads refer to the instance

2.1 deferred loading is applicable to concurrent scenarios: synchronized method

public class Singleton {
       
    private static Singleton INSTANCE = null;
    
    private Singleton() {
        
    }
    
    //	Problem: synchronized is a heavyweight lock. Locking the whole method (with a wide range of influence) will significantly reduce the concurrency performance
    public static synchronized Singleton getInstance() {
        if (INSTANCE == null) {
            
            INSTANCE = new Singleton();	
        }
        return INSTANCE; 
    }
}

2.2 delayed loading to improve concurrency performance: double detection

public class Singleton {
       
    private static Singleton INSTANCE = null;
    
    private Singleton() {
        
    }
    
    //	Double detection
    //	Problem: JVM instruction rearrangement 
    public static Singleton getInstance() {
        if (INSTANCE == null) {
            synchronized(Singleton.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Singleton();	
                }
            }
        }
        return INSTANCE; 
    }
}
What is JVM instruction rearrangement?

new Singleton() divides JVM execution into three steps (three instructions):

memory = allocate(); // 1. Allocate the memory space of the object
ctorInstance(memory); // 2. Initialization object
instance = memory; // 3. Set instance to point to the memory address just allocated

The compiler optimizes the instruction execution order and changes the order. For example, the execution order of 2 and 3 above is interchanged:

memory = allocate(); // 1. Allocate the memory space of the object
instance = memory; // 3. Set instance to point to the memory address just allocated
ctorInstance(memory); // 2. Object initialization

Before initializing the object, point instance to the address assigned to the object and bring it into the previous singleton static method to observe:

public class Singleton {
       
    private static Singleton INSTANCE = null;
    
    private Singleton() {
        
    }
    
    public static Singleton getInstance() {
        if (INSTANCE == null) {	// Statement 1
            synchronized(Singleton.class) {
                if (INSTANCE == null) {
                    //	When new Singleton() is executed, it is subdivided into the following three steps
                    /*memory = allocate();
					instance = memory; 
					ctorInstance(memory); */
                    INSTANCE = new Singleton();	//	Statement 2
                }
            }
        }
        return INSTANCE; 
    }
}

Thread A executes statement 2: instance = memory after execution; ctorInstance(memory) has not been executed;

At this time, thread B executes statement 1 and finds that INSTANCE is not empty, so it returns directly. At this time, INSTANCE is wrong

2.3 delay loading double detection and disable JVM instruction rearrangement: volatile keyword
public class Singleton {
    
    //	volatile add here
    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; 
    }
}

Summary: can I use direct loading or don't use lazy loading

Topics: Java Singleton pattern