1 Why singletons

There are classes in the system where only one instance is important. Such as:

  • Multiple printing tasks can exist in a system, but only one working task can exist
  • A system can have only one window manager or file system
  • A system can only have one timing tool or ID generator

How do you ensure that there is only one instance of a class that is easily accessible? Defining a global variable ensures that an object is always accessible, but it does not avoid instantiating multiple objects.

A better solution is to make the class itself responsible for keeping its unique instance. This class guarantees that no other instances will be created, and it can provide a method to access that instance. This is the motivation for the singleton pattern.

2 brief introduction

The singleton pattern ensures that a class has only one instance and that it instantiates and makes that instance available to the entire system. This class is called a singleton class, which provides globally accessible methods.

The key to the singleton pattern is as follows:

  • There can be only one instance of a class
  • Create this instance yourself
  • Provide this instance to the entire system yourself

The singleton pattern is an object creation pattern.

advantages

  • Having only one instance in memory reduces memory overhead, especially the frequent creation and destruction of instances
  • Avoid multiple resources (such as write file operations)

disadvantages

  • With no interfaces, no inheritance, and a conflict with the single responsibility principle, a class should only care about internal logic, not how it is instantiated externally.

Usage scenarios

  • Require production of unique serial numbers
  • Counters on the WEB, instead of being added to the database with each refresh, are cached with singletons first
  • Creating an object consumes too many resources, such as I/O connections to the database.

The getInstance() method uses the synchronized lock (singleton.class) to prevent instance instances from being instantiated multiple times.

  • A UML diagram of the singleton pattern

Analysis of the

The purpose of the singleton pattern is to ensure that a class has only one instance and to provide a global access point to it.

The singleton class has a private constructor that ensures that users cannot instantiate it directly with the new keyword.

During the implementation of the singleton pattern, note:

  • The constructor of a singleton class is private
  • Provide a static private member variable of its own;
  • Provide a public static factory method

Verify the existence of the instance and instantiate itself, then store it in a static member variable to ensure that only one instance is created.

advantages

  • Provides controlled access to a unique instance. Because a singleton class encapsulates its unique instance, it tightly controls how and when customers access it, and provides shared concepts for design and development teams
  • Since only one object exists in system memory, system resources are saved, and the singleton pattern can definitely improve system performance for objects that need to be created and destroyed frequently.
  • A variable number of instances is allowed. We can extend it based on the singleton pattern, using a method similar to singleton control to obtain a specified number of object instances.

disadvantages

  • Because there is no layer of abstraction in the singleton pattern, it is difficult to extend the singleton class
  • Singleton classes are too heavy on responsibilities and violate the “single responsibility principle” to some extent.

Because the singleton class acts both as a factory, providing factory methods, and as a product, containing business methods that blend the creation of the product with the functionality of the product itself.

  • Abuse of singletons will bring some negative problems, such as
    • In order to save resources, the database connection pool object is designed as a singleton class, which may lead to the overflow of the connection pool due to too many programs sharing the connection pool object
    • Many object-oriented languages now provide automatic garbage collection, so if an instantiated object is not used for a long time, the system will consider it garbage, destroy it and reclaim the resource, and then re-instantiate it the next time it is used, which will result in the loss of the object state.

# Applicable Scenarios

  • The system only needs one instance object, such as
    • The system requires a unique serial number generator
    • You need to consider the resource consumption and allow only one object to be created
  • A single instance of a customer invoking class is allowed to use only one public access point, and the instance cannot be accessed by any other means than that public access point.
  • The singleton pattern should only be used when only one instance of a class is required in a system. Conversely, if several instances of a class can coexist, the singleton pattern needs to be modified to become a multi-instance pattern

application

A table with auto-numbered primary keys can be used by multiple users at the same time, but only one place in the database can be assigned the next primary key number, otherwise primary key duplication will occur, so the primary key number generator must be unique, which can be implemented through the singleton pattern. # summary

  • The singleton pattern ensures that a class has only one instance and that it instantiates and makes that instance available to the entire system. This class is called a singleton class, which provides globally accessible methods. The singleton pattern has three main points: first, there can be only one instance of a class; Second, it must create the instance itself; Third, it must provide this instance to the entire system itself. The singleton pattern is an object creation pattern.
  • The singleton pattern contains only one singleton role: an internal implementation of a singleton class generates only one instance, and it provides a static factory method that allows customers to use its unique instance; To prevent instantiation externally, its constructor is designed to be private.
  • The purpose of the singleton pattern is to ensure that a class has only one instance and to provide a global access point to it. The singleton class has a private constructor that ensures that users cannot instantiate it directly with the new keyword. In addition, the pattern contains a static private member variable and a static public factory method. The factory method is responsible for verifying the existence of the instance and instantiating itself, then storing it in static member variables to ensure that only one instance is created.
  • The main advantages of the singleton pattern are that it provides controlled access to a unique instance and saves system resources. Its main disadvantages are that it is difficult to extend due to the lack of an abstraction layer and the singleton class is too responsible.
  • The singleton pattern applies when the system only needs one instance object; Only one common access point is allowed for a single instance of a client invoking class.

implementation

1 Lazy (not thread-safe)

The most basic implementation does not support multithreading. Synchronized is not strictly a singleton because it is not synchronized. Synchronized is lazy and does not require thread-safety. When multiple threads call getInstance() in parallel, multiple instances are created. That is to say, it will not work in multithreading.

public class Singleton {  
    private static Singleton instance;  
    private Singleton (a){}  
  
    public static Singleton getInstance(a) {  
       if (instance == null) {  
           instance = new Singleton();  
       }  
       returninstance; }}Copy the code

2 Lazy (thread-safe)

To solve the above problem, the simplest approach is to set the entire getInstance() method to the synchronized advantage: it is initialized only on the first call, avoiding memory waste. Disadvantages: synchronized must be added to ensure singletons, but locking will affect efficiency.

Although thread-safe and multi-instance problems are solved, it is not efficient. This is because only one thread can call getInstance() at any time, but synchronization is only needed on the first call, when the singleton object is first created. This brings us to the double check lock.

public class Singleton {
    private static volatile Singleton INSTANCE = null;
  
    // Private constructor suppresses 
    // default public constructor
    private Singleton(a) {}
  
    //thread safe and performance promote
    public static  Singleton getInstance(a) {
        if(INSTANCE == null) {synchronized(Singleton.class){
                 //when more than two threads run into the first null check same time, to avoid instanced more than one time, it needs to be checked again.
                 if(INSTANCE == null){ 
                     INSTANCE = newSingleton(); }}}returnINSTANCE; }}Copy the code

3 the hungry

More commonly used, but prone to generate garbage objects

  • Advantages: no lock, improve execution efficiency
  • Disadvantages: Class initialization on load, waste of memory

Quite simply, instances are declared as static and final variables and initialized the first time the class is loaded into memory, so creating the instance itself is thread-safe. However, instance is instantiated when the class is loaded. There are many reasons for class loading. In singleton mode, most of them call getInstance, but it is not certain that there are other ways (or static methods) to load the class. Instance initialization is obviously not lazy loading

 public class Singleton {
    private final static Singleton INSTANCE = new Singleton();
  
    // Private constructor suppresses   
    private Singleton(a) {}
 
    // default public constructor
    public static Singleton getInstance(a) {
        returnINSTANCE; }}Copy the code

If this were perfect, there would be no need to talk about double-checking. The disadvantage is that it is not a lazy initialization. The singleton is initialized as soon as the class is loaded, even if the client does not call the getInstance() method. Hungry type way of creation in some scenes will not be able to use: such as the creation of a Singleton instance is depend on the parameters or configuration file, the getInstance () method must be called before a set parameters to it, as the single example of writing can’t use.

4 Double checked locking pattern

A method of locking using synchronous blocks. Programmers call this a double-checked lock because instance == null is checked twice:

  • First time out of sync block
  • The second time is in the sync block

Why check again inside the synchronized block? Because it is possible for multiple threads to enter an IF outside the synchronized block at once, if no second check is performed inside the synchronized block, multiple instances will be generated.

public class Singleton {  
    private volatile static Singleton singleton; 
     
    private Singleton (a){}  
    
    public static Singleton getSingleton(a) {  
	    if (singleton == null) { // Single Checked
	        synchronized (Singleton.class) {  
	        if (singleton == null) { // Double Checked
	            singleton = newSingleton(); }}}returnsingleton; }}Copy the code

It looks perfect, but unfortunately, it still has problems. The point is that instance = new Singleton() is not an atomic operation, and in fact it does roughly three things in the JVM

  • 1, memory = allocate() Allocate memory space for objects
  • CtorInstance () calls the Singleton constructor to initialize a member variable
  • 3,instance = memorySet instance to the newly allocated memory and instance will be non-null.)

JVM and CPU optimizations, instruction reordering occurs but there is instruction reordering optimization in the JVM’S JIT. That is to say, the order of step 2 and step 3 above is not guaranteed, and the final execution order may be 1-2-3 or 1-3-2. If it’s the latter

  • 1, memory = allocate() Allocate memory space for objects
  • 3, Instance = memory Sets instance to the newly allocated memory
  • CtorInstance () initializes the object

Instance is already non-null (but not initialized), so thread 2 will return instance, use it, and then, of course, report an error.

Simply declare the instance variable as volatile

public class Singleton {
    private volatile static Singleton instance; // Declare as volatile
    private Singleton (a){}
    public static Singleton getSingleton(a) {
        if (instance == null) {                         
            synchronized (Singleton.class) {
                if (instance == null) {       
                    instance = newSingleton(); }}}returninstance; }}Copy the code

Some people argue that the reason for using volatile is visibility, that is, the ability to ensure that a thread does not have a local copy of an instance, instead fetching it from main memory each time. But that’s not true. The main reason for using volatile is another feature: it disallows instruction reordering optimizations. Read operations are not reordered before a memory barrier (in the generated assembly code) that follows assignments to volatile variables. For example, the fetch operation must be performed after 1-2-3 or 1-3-2. There is no case where the fetch operation is performed after 1-3. In terms of the “antecedent principle,” writes to a volatile variable occur first (in chronological order) before reads to it.

Note, however, that prior to Java 5, the use of volatile double-checking was problematic. The reason for this was that the JMM (Java Memory Model) prior to Java 5 was flawed, and even declaring variables volatile did not completely avoid reordering, primarily because the code before and after volatile variables still had reordering problems. The problem of volatile shielding reordering was only fixed in Java 5, so it was safe to use volatile after that.

You may not like this complicated and implicit approach, but there are better ways to implement thread-safe singleton patterns.

Static inner class

Thread-safe implementation difficulty: general description: this approach can achieve the effect of double check lock the same way, but the implementation is simpler to use lazy initialization, static field should be used this way rather than double check the lock This way only applies to the condition of the static field, double check the lock method can be used in instance fields need to be delayed initialization time. This method also uses the classloder mechanism to ensure that the instance is initialized with only one thread, which differs from the third method: The third way is that the instance is instantiated as long as the Singleton class is loaded (without the lazy loading effect). In this way, the instance is not necessarily initialized when the Singleton class is loaded. Because the SingletonHolder class is not actively used, instance is instantiated by explicitly loading the SingletonHolder class only when the getInstance method is explicitly called. Imagine if instantiating instance is expensive and you want it to load lazily, but on the other hand, you don’t want to instantiate it when the Singleton class loads because there’s no guarantee that the Singleton class could be used actively elsewhere and loaded. This is obviously not the time to instantiate instance. At this point, this approach seems more reasonable than the third one

public class Singleton {  
    private static class SingletonHolder {  
        private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (a){}  
    public static final Singleton getInstance(a) {  
        returnSingletonHolder.INSTANCE; }}Copy the code

6 the enumeration

JDK5, thread-safe implementation of the singleton pattern best approach. The most concise, automatic support serialization mechanism, absolutely prevent multiple instances. Effective Java author Josh Bloch advocates this approach:

  • Avoid multithreaded synchronization problems
  • Automatic support for serialization
  • Prevents deserialization from recreating new objects
  • Absolutely prevent multiple instantiations
  • Private constructors cannot be called by reflection intrusion
public enum Singleton {  
    INSTANCE;  
    public void whateverMethod(a) {}}Copy the code

experience

Not lazy, but hungry. Use type 5 only when lazy loading is explicitly implemented. Enumeration is recommended when deserializing objects. For other special needs, DCL can be considered.