Several implementations of the singleton pattern (continuous update)

Posted by webwannabee on Sat, 17 Aug 2019 15:22:49 +0200

Singleton Pattern is one of the simplest design patterns in Java. This type of design pattern belongs to the 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 the class.

Be careful:

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

introduce

** Intention: ** Guarantees that a class has only one instance and provides a global access point to access it.

** The main solution is: ** A globally used 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 this problem: ** Determine whether the system already has this singleton, return if there is one, and create if not.

** Key Code: ** The constructor is private.

Application examples:

  • 1. There is only one head teacher in a class.
  • 2. Windows is multi-process and multi-threaded. When operating a file, it is inevitable that multiple processes or threads operate a file at the same time. Therefore, the processing of all files must be carried out through a unique instance.
  • 3. Some device managers are often designed as singletons. For example, a computer has two printers. When it outputs, it is necessary to deal with the problem that two printers cannot print the same file.
  • 4. The whole window has only one time component

Advantage:

  • 1. There is only one instance in memory, which reduces the memory overhead, especially the frequent creation and destruction of instances (such as the management school homepage cache).
  • 2. Avoid multiple resource usage (e.g. file writing).

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

Use scenarios:

  • 1. Require the production of unique serial number.
  • 2. Counters in WEB are added to the database without every refresh. They are cached first with singletons.
  • 3. Creating an object consumes too much resources, such as the connection between I/O and database.

** Notes: ** Synchronized lock (Singleton. class) is required in the ** getInstance() method to prevent multithreading from entering simultaneously, resulting in instance being instantiated many times.

Several ways of implementation

Lazy-man Style

Lazy-handed means that objects are created only when they are first used.

public class Singleton {
    //Combining one's own object reference, private protects it from being modified by direct access, and static protects the uniqueness of this class
    private static Singleton singleton;

    //Privatization of constructors so that objects of this type cannot be created from outside
    private Singleton() {
    }

    //Static methods for obtaining singleton instances as external access points
    //Modifying synchronized to synchronized ensures that only one object is created in a multithreaded environment
    public static synchronized Singleton getInstance() {
        return null == singleton ? singleton = new Singleton() : singleton;
    }
}

In this way, because the external access point is set to synchronize, the work efficiency in multi-threaded environment is very low. When synchronization of getInstance() operations is not critical to the performance of the entire system, this approach can be used to avoid memory waste caused by hungry people creating the object.

Hungry Chinese Style

The hungry Chinese style creates this object only when the class is loaded, but it should be noted that the hungry Chinese style does not necessarily lazy to load. You can think of lazy loading as creating this object the first time you call the getInstance() method. Lazy loading guarantees lazy loading and hungry loading does not.

public class Singleton {
    //Create objects when classes are loaded, hungry Chinese
    private static Singleton singleton=new Singleton();

    //Privatization of constructors so that objects of this type cannot be created from outside
    private Singleton() {
    }

    //When this method is called, the class must have been loaded and returned directly.
    //No synchronized synchronization is required
    public static Singleton getInstance() {
        return singleton;
    }
}

In this way, the Java class loading process is skillfully used to ensure thread safety, because this class is loaded only once, so this object must be unique. And because there is no synchronized restriction on access points, this way of access efficiency is relatively high.

In addition to memory wastage, it should be noted that ** "Class Loading" is not necessarily the first call to the access point **, because there may be other static methods in the class that have been called before leading to the creation of objects, so hungry people can not guarantee lazy loading, probably as early as when other static methods are called. Objects are created.

Double check lock mode

Double Checked Locking combines the advantages of lazy and hungry Chinese. It neither wastes memory (lazy loading) nor degrades the performance of external access points too much.

public class Singleton {
    //volatile guarantees that when a thread modifies the variable, the cached row of the variable in another thread is invalid and read directly into memory.
    //In summary, if a thread modifies the value of a variable, the new value is immediately visible to other threads and is used for checking in the access point.
    private volatile static Singleton singleton;

    //Privatization of constructors so that objects of this type cannot be created from outside
    private Singleton() {
    }

    //Using DCL locks to ensure thread safety does not require synchronized synchronization of the entire method
    public static Singleton getInstance() {
        //If the thread finds that the object is not created
        if (null == singleton) {
            //So first you have to compete with other threads for locks of this class
            synchronized (Singleton.class) {
                //Only after the lock is acquired can this part of the code be executed.
                //At this point, check again if it is null.
                //If it's still null, it means that it's the first one to compete for locks. This thread is responsible for creating objects.
                if (null == singleton)
                    singleton = new Singleton();
                //If it's not null, it means that the lock has been used by others and released after creating a good object.
                //At this point, the object has been created, this thread does nothing, just release the lock.
            }
        }
        //At this point, the object must have been created uniquely and returned directly.
        return singleton;
    }
}

Registration Form

Registered is a thread-safe container (many online registrations use HashMap, which is thread-insecure, Concurrent HashMap is the right choice) to register instances that need to be singularized, and when used directly from the container.

You can set up a single class to manage the singleton object to be registered, or you can set up a registration container for the singleton object class itself. Let's demonstrate the former by setting up a separate class to manage registration.

//Singleton Management Class
public class SingletonManager {
    //Thread-safe containers, hungry Chinese guarantees container objects as an individual
    private static Map map = new ConcurrentHashMap();

    //External access point, passing in the class name, returns the class's singleton object. The class is registered into the container above for singleton management.
    //In the class must ensure that the construction method privatization, this management class is uncontrollable, you need to guarantee their own.
    public static Object getInstance(String className) {
        //If not registered in the container
        if (!map.containsKey(className)) {
            //Create objects reflectively (because the constructor has been privatized) and register them in containers
            try {
                map.put(className, Class.forName(className).newInstance());
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        //Get the managed singleton object from the container and return it
        return map.get(className);
    }
}

Static inner class

Even with static internal classes, this approach is an improvement on the hungry-man style. Since hungry Chinese style can create objects when classes are loaded, which can not guarantee lazy loading, it is advisable to add a static inner class and call the static inner class only in the access point method, so that the creation of objects when the static inner class is loaded will not be affected by other static methods.

public class Singleton {
    //Privatization of constructors so that objects of this type cannot be created from outside
    private Singleton(){

    }

    //This avoids the creation of hungry Chinese style.
    private static class singletonHolde{
        //This object will not be created when the external class is loaded to avoid being affected by other static methods of the external class.
        private static Singleton singleton = new Singleton();
    }

    //External access points
    public static Singleton getInstance(){
        //The object is created only when the internal class is first referenced, and lazy loading is achieved as long as it is guaranteed to be in this method.
        return singletonHolde.singleton;
    }
}

enumeration

The enumerated constructor itself is private, and can be serialized freely, thread-safe, and guarantee the singleton. Enumeration is the best way to implement the singleton pattern. In fact, enumeration is a final class. It can also have other attributes and methods. It is very simple to use enumeration as a common class to implement singletons.

public enum Singleton {
    INSTANCE;//Enumeration objects are naturally singletons

    //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 out
public class Main {
    public static void main(String[] args) {
        Singleton.INSTANCE.sayId();
        System.out.println(Singleton.INSTANCE == Singleton.INSTANCE);
    }
}

Output:

id yes2019
true

If you have any questions, you can join the group to discuss learning together.

Topics: Java Database Windows