Design mode -- single case mode (multiple implementation modes)

Singleton mode and multiple implementation methods


Singleton Pattern is one of the simplest design patterns in Java. This type of design pattern is a creation pattern, which provides the best way to create objects.

This pattern involves a single class that is responsible for creating its own objects, while ensuring that only a single object is created. This class provides a way to access its unique object, which can be accessed directly without instantiating the object of this class.

be careful:

  • 1. A singleton class can have only one instance.
  • 2. A singleton class must create its own unique instance.
  • 3. Singleton classes must provide this instance to all other objects.

introduce

Intent: ensure that a class has only one instance, and provide a global access point to access it.

Main solution: a global class is frequently created and destroyed.

When to use: when you want to control the number of instances and save system resources.

How to solve: judge whether the system already has this single instance. If there is one, return it. If not, create it.

Key code: the constructor is private.
Application example:

  • 1, There is only one head teacher in a class.
  • 2. Windows is multi process and multi thread. When operating a file, it is inevitable that multiple processes or threads operate a file at the same time. Therefore, all files must be processed through a unique instance.
  • 3. Some device managers are often designed as a single example mode. For example, a computer has two printers. When outputting, it is necessary to process that two printers cannot print the same file.
  • 4. There is only one time component in the whole window

advantage:

  • 1. There is only one instance in the memory, which reduces the memory overhead, especially the frequent creation and destruction of instances (such as the cache of homepage page of Management College).
  • 2. Avoid multiple uses of resources (such as file writing).

Disadvantages: no interface, no inheritance, and conflict with single responsibility principle. A class should only care about internal logic, not how to instantiate outside.

Usage scenario:

  • 1. Production unique serial number is required.
  • 2. The counters in WEB do not need to be added to the database every time they are refreshed. They are cached first with a single instance.
  • 3. Creating an object consumes too many resources, such as the connection between I/O and database.

Note: synchronized lock is required in getInstance() method( Singleton.class )Prevent multithreading from entering at the same time, causing instance to be instantiated multiple times.

Several implementation methods

Slovenly

Lazy is when you create an object for the first time.

public class SingletonClass {
    //Combine an object reference of its own. private protects it from direct access and modification. static protects the uniqueness of this class
    private static SingletonClass singletonClass;

    //The constructor is privatized so that objects of this class cannot be created from outside
    private SingletonClass() {
    }

    //Static method of obtaining single instance as external access point
    //Modifying synchronized to synchronous ensures that only one object can be created in a multithreaded environment
    public static synchronized SingletonClass getInstance() {
        return null == singletonClass ? singletonClass = new SingletonClass() : singletonClass;
    }
}

In this way, because the whole external access point is set to be synchronous, the efficiency of multithreading environment is very low. When the synchronization of getInstance() operation is not critical to the performance of the whole system, you can use this method if you need to avoid the memory waste caused by the hungry man creating the object.

Hungry Han style

This object is created when the class is loaded, but it should be noted that the hungry type is not necessarily lazy to load. Lazy loading can be regarded as creating this object when the getInstance() method is called for the first time. Lazy loading can guarantee lazy loading, while hungry loading cannot.

public class SingletonClass {
    //Create the object when the class is loaded, hungry Chinese style
    private static SingletonClass singletonClass=new SingletonClass();

    //The constructor is privatized so that objects of this class cannot be created from outside
    private SingletonClass() {
    }

    //When calling this method, the class must have been loaded. Return directly
    //No synchronized synchronization required
    public static SingletonClass getInstance() {
        return singletonClass;
    }
}

In this way, Java's class loading process is cleverly used to ensure thread safety. Because this class is only loaded once, this object must be unique. And because there is no limited access point of synchronized, the access efficiency of this way is relatively high.

In addition to paying attention to memory waste, we should also note that "class loading time" is not necessarily the first time to call access points, because there may be other static methods in the class that cause objects to be created before, so the starving style can not guarantee lazy loading, and it may have been created before calling other static methods.

Double check lock mode

With double checked locking, you can combine the advantages of lazy type and hungry type. It doesn't waste memory (lazy loading), and it doesn't make the performance of external access point degrade too much.

public class SingletonClass {
    //volatile ensures that when a thread modifies the variable, the cache line of the variable in another thread is invalid and is read directly to memory
    //In a word, if a thread modifies the value of a variable, the new value will be immediately visible to other threads, and will be used when checking in the access point
    private volatile static SingletonClass singletonClass;

    //The constructor is privatized so that objects of this class cannot be created from outside
    private SingletonClass() {
    }

    //Using DCL lock to ensure thread safety does not need to synchronize the whole method
    public static SingletonClass getInstance() {
        //If the thread finds that the object is not created
        if (null == singletonClass) {
            //First, we need to compete with other threads for locks of this class
            synchronized (SingletonClass.class) {
                //This part of the code can only be executed after the lock is obtained
                //Check again if it is null
                //If it is still null, it means that it is the first one competing for the lock. This thread is responsible for creating the object
                if (null == singletonClass)
                    singletonClass = new SingletonClass();
                //If it's not null, it means that the lock has been used by others and released after the object is created
                //At this time, the object has been created. This thread does not need to do anything. Just release the lock
            }
        }
        //At this point, the object must have been created uniquely and returned directly
        return singletonClass;
    }
}

Registration form

The registration type is to use a thread safe container (many online registration types use HashMap, which is thread unsafe, and concurrent HashMap is the right choice) to register the instance to be singleton. When using, you can take it out of this container directly.

You can set a separate class to manage the single instance object to be registered, or you can set the registration container for the single instance object class. Let's demonstrate the former, which is to set up a separate class to manage the registration.

//Single case management
public class SingletonManager {
    //Thread safe container, starving Chinese to ensure that the container object itself is a single example
    private static Map map = new ConcurrentHashMap();

    //The external access point, passing in the class name, returns the singleton object of the class. The class will be registered in the above container for singleton management
    //It is necessary to ensure the privatization of the construction method in the class. For this point, the management class cannot control and needs to guarantee by itself
    public static Object getInstance(String className) {
        //If it's not registered in the container
        if (!map.containsKey(className)) {
            //Create the object in a reflective way (because the constructor has been privatized) and register it in the container
            try {
                map.put(className, Class.forName(className).newInstance());
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        //Get managed singleton from container and return
        return map.get(className);
    }
}

Static inner class

That is to say, static inner classes are used to implement this method, which is an improvement on the starved Chinese style. Since the hungry Chinese type creates objects on the class loading, resulting in no guarantee of lazy loading, it is possible to add a static internal class and invoke the static inner class only in the access point method, so that when the static internal class is loaded, creating objects will not be affected by other static methods.

public class Singleton {
    //The constructor is privatized so that objects of this class cannot be created from outside
    private Singleton(){

    }

    //In this way, we can avoid the creation of the hungry Han style
    private static class singletonHolde{
        //This object will not be created when the external class is loaded, avoiding being affected by other static methods of the external class
        private static Singleton singleton = new Singleton();
    }

    //External access point
    public static Singleton getInstance(){
        //This object will be loaded and created only when the internal class is referenced for the first time. As long as this must be in this method, lazy loading is achieved
        return singletonHolde.singleton;
    }
}

enumeration

The enumeration constructor itself is private, and it can be serialized freely, thread safe, and single instance guaranteed. Enumerations are the best way to implement singleton patterns. Before, I didn't know much about enumeration. In fact, enumeration is a final class. It can also have other properties and methods. It's very simple to use enumeration as a common class to implement a single instance.

public enum Singleton {
    INSTANCE;//Enumerating objects is a natural example

    //Enumeration classes can also have other properties
    private int id = 2019;

    //Enumeration classes can also have other methods
    public void sayId() {
        System.out.println("id yes" + id);
    }
}

//Test it
public class Main {
    public static void main(String[] args) {
        Singleton.INSTANCE.sayId();
        System.out.println(Singleton.INSTANCE == Singleton.INSTANCE);
    }
}

Output:

id It's 2019
true

Reflection can break the above several (excluding enumeration) implementations! (exception control can be thrown manually in the constructor)
Deserialization can break the above several (excluding enumeration) implementation methods!
Enumeration is the official recommended way to use!

Tags: Java Database Windows

Posted on Wed, 17 Jun 2020 03:02:52 -0400 by wardy