[design mode: single case mode] single case mode 02: lazy mode

Keywords: Java Design Pattern Singleton pattern

Singleton mode 02: lazy mode

Text start@Assassin

1. Review the hungry Han model:

Briefly review the hungry man mode in a single case mode~
I'm hungry man mode!

The so-called hungry man mode means that as long as the class is loaded, the object gf will be created. Even if GF is not used, it will be created. This is the loading mechanism of the class: as long as the static method or attribute is called, the class where the method or attribute is located will be loaded. After the class is loaded, the static attribute and static code block will be initialized first.

Take a vulgar example. Say you haven't been with gf for a long time. Before you two can get to know each other, it has become your girlfriend. That is, it's too hasty to act like a hungry man. This is the popular understanding of the hungry man model.

Let's take chestnuts for example:

package com.haut.iot.assassin;

//One class is GirlFriend
//If you can only have one girlfriend
class GirlFriend {
    private String name; //full name
    public static int n = 1; //Static variable
    //How to ensure that we can only create one GirlFriend object?
    //[singleton mode - > hungry man mode]
    //1. Privatization of constructor
    //2. Create an object inside the class (the object must be static)
    //3. Provide a static public method to the outside
    private static GirlFriend gf = new GirlFriend("Wang Zuxian");//Private object

    //To return gf in getInstance(), modify the method to static
    public static GirlFriend getInstance() {
        return GirlFriend.gf; //Return gf
    }

    private GirlFriend(String name) { //constructor 
        System.out.println("I'm a constructor, I'm called!");
        this.name = name;
    }

    @Override
    public String toString() { //Override toString()
        return "GirlFriend{" +
                "name='" + name + '\'' +
                '}';
    }
}

public class SingleTon {
    public static void main(String[] args) {
        System.out.println(GirlFriend.n); //Only the static variable n is used, not the gf object
    }
}

You can see that a static variable n is defined in the class

At this time, use the class variable GirlFriend.n in the test class. We should know that when class variables are used, the class loader will perform class loading. The first step of class loading is to initialize static variables and static code blocks, as shown in the following figure:

So private static GirlFriend gf = new GirlFriend("Wang Zuxian"); Will be executed, that is, the GirlFriend object is created.
The corresponding constructor will also be called, and the verification is as follows:

You can see that the constructor is indeed called, but the gf object is not used, which well verifies the disadvantages of the hungry man pattern: there is no need to use, and the object is also created

2. Lazy mode details:

The general implementation structure of lazy mode is as follows:

  • Constructor privatization
  • Create a static object reference without directly creating a new object
  • Provide a public static method to get a Cat object
  • Lazy mode returns the object only when the programmer uses the getInstance() method, and the original object will be returned when calling again later, so as to ensure the singleton

Look at this Code: you can see that the object reference of static does not reference an instance, that is, no object is created here.
That is, even if the class is loaded at this time, the constructor will not be called. The call of the constructor is called with the creation of the object. Its function is to initialize the created object.

Similarly, let's verify:

package com.haut.iot.ninghai;

//You want to create only one object while the program is running
//Use singleton mode
class Cat {
    private String name;//name
    private static Cat cat; //The default is null
    public static int n = 100;//Static variable
    //1. Privatization of constructor
    //2. Create a static object
    //3. Provide a public static method to get a Cat object
    private Cat(String name) {//Constructor, we still modify it as private
        System.out.println("I'm a constructor. I'm called~");
        this.name = name;
    }
    //getInstance() method
    public static Cat getInstance() {
        if (cat == null) {
            cat = new Cat("little cat");
        }
        return cat;
    }
}

//Demonstrate lazy singleton mode
public class SingleTon {
    public static void main(String[] args) {
        System.out.println(Cat.n);
    }
}

A public static class variable n is also defined in the class

Call n by class name in the test class

The operation results are as follows:

It can be clearly seen that the value of n is 100, and the constructor is not called to print the statement:

In other words, although the class has been loaded at this time, no object has been created, which is essentially different from the hungry man mode. Let's compare it ourselves~

When can we create objects?
Just call getInstance() yourself~~
As shown in the figure below:

Execution results:

Source code:

package com.haut.iot.ninghai;

//You want to create only one object while the program is running
//Use singleton mode
class Cat {
    private String name;//name
    private static Cat cat; //The default is null
    public static int n = 100;//Static variable
    //1. Privatization of constructor
    //2. Create a static object
    //3. Provide a public static method to get a Cat object
    private Cat(String name) {//Constructor, we still modify it as private
        System.out.println("I'm a constructor. I'm called~");
        this.name = name;
    }
    //getInstance() method
    public static Cat getInstance() {
        if (cat == null) {
            cat = new Cat("little cat");
        }
        return cat;
    }
}

//Demonstrate lazy singleton mode
public class SingleTon {
    public static void main(String[] args) {
        System.out.println(Cat.n); //Class variable
        Cat instance = Cat.getInstance(); //Get a Cat object
    }
}

Why is that all right?

Let's analyze a wave:
It can be clearly seen that the logic in getInstance() is as follows: if cat is null, a new object will be created. If it is not empty, the original object will be returned directly. Because cat is null by default, a cat object will be created when calling getInstance() for the first time, and then the call will return the source object, ensuring that a class has only one instance object.

Let's practice the usual routine:

package com.haut.iot.ninghai;

//You want to create only one object while the program is running
//Use singleton mode
class Cat {
    private String name;//name
    private static Cat cat; //The default is null
    public static int n = 100;//Static variable
    //1. Privatization of constructor
    //2. Create a static object
    //3. Provide a public static method to get a Cat object
    private Cat(String name) {//Constructor, we still modify it as private
        System.out.println("I'm a constructor. I'm called~");
        this.name = name;
    }
    //getInstance() method
    public static Cat getInstance() {
        if (cat == null) {
            cat = new Cat("little cat");
        }
        return cat;
    }

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                '}';
    }
}

//Demonstrate lazy singleton mode
public class SingleTon {
    public static void main(String[] args) {
        //System.out.println(Cat.n); // Class variable
        Cat instance1 = Cat.getInstance(); //Get a Cat object
        System.out.println(instance1);
        Cat instance2 = Cat.getInstance();//Singleton mode, same object
        System.out.println(instance2);
        //true
        System.out.println(instance1 == instance2);
    }
}

Operation results:

3. Lazy mode VS hungry mode:

Hungry VS lazy

  • The main difference between the two is that the time of creating objects is different: the hungry type creates object instances when the class is loaded, while the lazy type creates objects when they are used
  • There is no thread safety problem for the hungry type, and there is thread safety problem for the lazy type
  • There is the possibility of wasting resources. Because if the programmer does not use any object instances, the objects created by the hungry type will be wasted, and the lazy type will be created only when used, so this problem does not exist
  • In the javaSE standard class, java.lang.Runtime is the classic singleton pattern (hungry man pattern)

A simple analysis:
The thread safety problem of lazy mode is mainly reflected in the getInstance() method.
Suppose that three threads execute getInstance() method at the same time. All three threads enter the judgment of if statement. When the first thread judges null, the second thread also quickly executes the judgment of if condition. Suppose that when the second thread enters the if and the first thread has not created the object, the second thread will also create a new object because it is judged to be null at this time. The same is true for the third thread. This will cause multiple objects to be created at the same point in time, which obviously breaks the rules of singleton mode. This thread safety problem can be solved. We'll talk about ⑧ later

The hungry man mode will cause a waste of resources because you may only need to use another module, but the class loading mechanism will do a good job (create objects) that you don't need, resulting in a waste of resources. The lazy man must call himself to create objects. There is no waste of resources.

In JavaSE, there is a classic singleton pattern in java.lang.Runtime. Let's take a look at the source code:

Obviously, this is a hungry man's singleton mode.

Posted by Coldman on Tue, 02 Nov 2021 03:08:05 -0700