The four principles of singletons:

  1. Construct private.
  2. Returns instances as static methods or enumerations.
  3. Make sure there is only one instance, especially in multithreaded environments.
  4. Ensure that the object is not rebuilt when the antisequence is changed.

The main singleton patterns are DCL and static inner class lazy loading

DCL (Double-checked lock)

public class SingleTon {

    private int mVal;
    
    private static volatile SingleTon sInstance;

    private SingleTon(int val) {
        mVal = val;
    }

    public static SingleTon getInstance(a) {
        if (sInstance == null) { // The first judgment
            synchronized (SingleTon.class) {
                if (sInstance == null) { // Second judgment
                    sInstance = new SingleTon(1); }}}return sInstance;
    }

    public void print(a) {
        Log.e("SingleTon"."Val = "+ mVal); }}Copy the code

The double check lock has two tests to determine whether sInstance is null. The following uses thread A and thread B to simulate the run, to illustrate the necessity of two tests.

  1. Thread A runs until the first judgment returns true, at which point thread B starts running.

  2. Thread B also runs until the first judgment, which is true because sInstance has not yet been created, and then runs the following code, using synchronized, until the second judgment, at which point even if thread B stops and thread A runs, thread A is still blocked because of the lock. Again, wait until thread B releases the lock, then thread B creates the object and releases the lock.

  3. Thread A, in turn, returns false on the second judgment, so no more objects are created.

Another key point of the DCL is the use of volatile, which takes advantage of its visibility and non-reordering features.

  • Visibility interpretation

When a variable is volatile, changes made by one thread are immediately flushed to main memory, and when other threads need to read the variable, the new value is read from memory.

Thread A: Load sInstance into its own working memory, where sInstance is null. If thread B assigns A value to sInstance, it will be immediately flushed to main memory for volatile reasons. If thread A loads sInstance again to its own working memory, it will not be null

If volatile had not been used, thread B might not have had time to flush the sInstance into main memory. Thread A will not re-read the main memory for the second time. So visibility comes into play.

  • Disables reordering of feature interpretation

The sInstance = new SingleTon(1) line actually has three steps

  1. Space is requested in the heap memory.
  2. The mVal parameter is assigned.
  3. Object and points to the heap memory space.

If sInstance == null is false, but val has not been assigned to it, then another thread may use the unassigned mVal, causing problems. So the prohibition on reordering works.

Internal static class

public class SingleTon {

    private int mVal;

    private SingleTon(int val) {
        mVal = val;
    }

    private static class SingleTonHolder {
        private static final SingleTon INSTANCE = new SingleTon(2);
    }

    public static SingleTon getInstance(a) {
        return SingleTonHolder.INSTANCE;
    }

    public void print(a) {
        Log.e("SingleTon"."Val = "+ mVal); }}Copy the code

To gain a deeper understanding of how internal static classes implement singletons, you need to know the class loading process (load validation preparation parsing initialization)

The class initialization method,

(), is set to initial values for class variables and is the last piece of work before a class or interface is used for the first time.

When

() is called: The class is initialized as follows:

  • The first time you create a new instance of a class –new, reflection, cloning, or deserialization;
  • The first time a static method of a class is called;
  • When a static field of a class or interface is used for the first time or a value is assigned to it (except for final fields);
  • When some of Java’s reflection methods are first called;
  • When you first initialize a subclass of a class;
  • The boot class that contains the main() method when the virtual machine first starts

When getInstance() is called, singletonholding.instance is called, because SingleTonHolder’s static field INSTANCE is used for the first time, so the

() method of SingleTonHolder is called by the virtual machine. To assign a value to INSTANCE.

The virtual machine ensures that the

() method of a class is properly locked and synchronized in a multi-threaded environment. If multiple threads initialize a class at the same time, only one thread will execute the class’s

() method, and the other threads will block and wait until the active thread finishes executing the () method. A type is initialized only once under the same loader.