Design pattern_singleton pattern (singleton)

Design pattern_singleton pattern (singleton)

Singleton mode: Only one instance can be created, and one instance is enough for development business. In this case, it is recommended to use singleton mode.
Such as: tool class, resource class.

1. How to write singleton mode
Singleton mode includes lazy man, hungry man, enumeration, static inner class, and double check lock.
Here I only write about two commonly used ones: hungry man and static inner class. The accompanying ones will be compared with some others but will certainly not be comprehensive.
This method
avoids multi-thread synchronization problems based on the classloder mechanism. However, the instance is instantiated when the class is loaded. In the singleton mode, most of the methods call the getInstance method. At this time, the initialized instance obviously does not reach lazy loading ( lazy loading) effect. This is suitable for situations where the class needs to be loaded from the beginning

public class Car {
    private static final Car car = new Car();

    private Car(){}

    public static  Car getInstance(){
        return car;
    }

    public void driveCar(int index){
        System.out.println("驾驶员已经启动"+index+"汽车");
    }
}

To compare, you can look at the writing method of static inner classes. This method of
static inner classes
also uses the classloder mechanism to ensure that there is only one thread when initializing the instance. It is different from the Hungry method (very subtle difference): Hungry method The method is that as long as the Singleton class is loaded, the instance will be instantiated (the lazy loading effect is not achieved). In this method, the Singleton class is loaded, and the instance is not necessarily initialized. Because the SingletonHolder class is not actively used, the SingletonHolder class will only be explicitly loaded by calling the getInstance method to instantiate the instance. Imagine if instantiating an instance consumes resources and I want it to be loaded lazily. On the other hand, I don't want to instantiate it when the Singleton class is loaded, because I cannot ensure that the Singleton class may be actively used in other places. is loaded, then it is obviously inappropriate to instantiate instance at this time. At this time, this method seems very reasonable compared to the hungry man method.

public class CarStaticClass {
    private static class CarStaticClassHolder{
        private static CarStaticClass car = new CarStaticClass();
    }
    private CarStaticClass(){};

    public static final CarStaticClass getInstance(){
        return CarStaticClassHolder.car;
    }
}

The above two are the most commonly used ways of writing. Here I am writing about a lazy man’s way and some things to pay attention to in the lazy man’s way.
Lazy (thread-unsafe)
method. Generally, multiple objects will be created when multiple threads call it at the same time. Obviously, the purpose of a singleton is not achieved. Because Car is empty when called by a thread (A). Car is also empty when called by another thread (B). Then two Car objects will be created at the same time. That is, two Car objects are returned. Therefore, this way of writing generally fails to achieve the purpose of Singleton. But it is obviously lazy loading, but it can only be passed due to thread insecurity.

/*实例化类*/
public class Car {
    
    
    private static Car car;

    private Car(){}

    public static synchronized Car getInstance(){
        if(car == null){
            System.out.println("创建Car对象");
            car = new Car();
        }
        return car;
    }

    public void driveCar(int index){
        System.out.println("驾驶员已经启动汽车");
    }
}

/*========================================*/
/*调用类这里可以看出多线程的方式调用。代码都比较简单就没写注释了*/
public class Client extends Thread{
    
    

    public void run(){
        for(int i =0 ;i<5; i++){
            Car car = Car.getInstance();
            car.driveCar(i);
        }
    }


    public static void main(String args[]){
        Client client1 = new Client();
        Client client2 = new Client();
        client1.start();
        client2.start();
    }
}

/*========================================*/
/*运行结果*/
创建了Car
创建了Car
驾驶员已经启动0汽车
驾驶员已经启动0汽车
驾驶员已经启动1汽车
驾驶员已经启动2汽车
驾驶员已经启动3汽车
驾驶员已经启动4汽车
驾驶员已经启动1汽车
驾驶员已经启动2汽车
驾驶员已经启动3汽车
驾驶员已经启动4汽车

Lazy (thread-safe)
has few code changes compared to thread-unsafe and adds a lock. It also has the effect of lazy loading. But what I’m telling you is that this method is very inefficient.

public class Car {
    private static final Car car = new Car();

    private Car(){}

    public static synchronized  Car getInstance(){
        return car;
    }

    public void driveCar(int index){
        System.out.println("驾驶员已经启动"+index+"汽车");
    }
}

As for the other ways. It seems to be rarely seen in actual work. Here are just some simple instructions. The enumeration method is relatively rare. I think its main function is to prevent deserialization from re-creating new objects. If there is an instantiation requirement, the serialization interface can be implemented.
I don't think the double check lock method is necessary. If it is not a specific occasion, there is really no need to use double check locking. For details, please see
2. Explanation of
why the singleton mode can become a singleton. It may not be much different if the code is normal. Dear friends, look at the construction method of the singleton pattern. Its construction method is parameterless and private. This will ensure that external objects cannot be created through new. It can only be constructed through the getInstance method. In this method is the returned object, which has been initialized. And it's final and static. No new objects will be generated in this way.
As for whether to use Hungry Man or static inner classes, it depends on the scenario. Generally, Hungry is used unless static inner classes are explicitly used. Be sure to ensure thread safety when using it.

Guess you like

Origin blog.csdn.net/my_God_sky/article/details/52201222