Java Design Patterns - Detailed Explanation of Singleton Pattern (Singleton Pattern)

An overview of the singleton design pattern

1. Introduction to the singleton pattern

The Singleton Pattern is one of the most common and simplest design patterns in Java. This design pattern is one of five creational types that provide an optimal 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 only object, which can be accessed directly, and cannot be instantiated directly from outside the class, that is, the new keyword cannot be used directly for instantiation outside the class, but through This class provides a public static method to obtain an instance of the object.

Notice:

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

2. Basic knowledge of singleton pattern

2.1, the basic idea

A class can return a reference to the object (always a reference to the same object, the same address) and a method (static method) to get the instance, when we call this method, if the reference held by the class is not null, it returns For this reference, if the reference held by the class is empty, an instance of the class is created and the reference of the instance is assigned to the reference held by the class; at the same time, the constructor of the class needs to be privatized (modified with private), so that the code elsewhere It is impossible to instantiate the object of the class by calling the constructor of the class (you cannot use the new keyword to instantiate), and only the static method provided by the class can be used to obtain the only instance of the class.

The principle and process of realizing the simple interest model: 

  • 1. Singleton mode: ensure that a class has only one instance, instantiate it by itself, and provide this instance to the system 
  • 2. Classification of singleton mode: hungry singleton mode (instantiate an object for its own reference when the class is loaded), lazy singleton mode (the object will only be instantiated when the method of obtaining an instance such as getInstance is called) (the hungry singleton in java) Mode performance is better than lazy singleton mode, lazy singleton mode is generally used in c++) 
  • 3. Three elements of the singleton pattern: a. private constructor ,   b. private static reference pointing to its own instance, c. public static method with its own instance as the return value

2.2. Features

The privatization of the constructor of a singleton class ensures that a class has one and only one instance, and provides a global access point to access it, that is, a static method for internal instantiation.

2.3. Main solution scenarios and usage scenarios

Determine whether the system has an instance of a singleton class, and if so, return the instance without instantiating each part; eliminate the frequent creation and destruction of a globally used class, which affects the performance of the system; the singleton mode can control the number of instances and save the system resource time.

Applicable scenarios: 
The singleton mode only allows one object to be created, so it saves memory and speeds up object access. Therefore, objects need to be used in common occasions, such as multiple modules using the same data source to connect objects, etc. Such as: 
    1. Objects that need to be frequently instantiated and then destroyed. 
    2. Objects that take too much time or consume too many resources when creating objects, but are often used. 
    3. Stateful utility objects. 
    4. Objects that frequently access databases or files.

2.4, the advantages and disadvantages of the singleton pattern

advantage:

    • 1. In the singleton pattern, the active singleton has only one instance, and all instantiations of the singleton class get the same instance. This prevents other objects from instantiating themselves, ensuring that all objects access an instance 
    • 2. The singleton mode has certain scalability. The class itself controls the instantiation process, and the class has corresponding scalability in changing the instantiation process. 
    • 3. Provides controlled access to unique instances. 
    • 4. Since there is only one object in the system memory, system resources can be saved, and the singleton mode can undoubtedly improve the performance of the system when objects need to be frequently created and destroyed. 
    • 5. Allow a variable number of instances. 
    • 6. Avoid multiple occupation of shared resources.

shortcoming:

  •   1. It is not suitable for changing objects. If objects of the same type always change in different use case scenarios, the singleton will cause data errors and cannot save each other's state. 
  •   2. Since there is no abstraction layer in the simple interest mode, it is very difficult to extend the singleton class. 
  •   3. The responsibility of the singleton class is too heavy, which violates the "single responsibility principle" to a certain extent. 
  •   4. Abusing singletons will bring some negative problems. For example, in order to save resources, the database connection pool object is designed as a singleton class, which may lead to too many programs sharing the connection pool object and connection pool overflow; if the instantiated object If it is not used for a long time, the system will consider it as garbage and be recycled, which will lead to the loss of object state.

2.5, singleton mode attention points

The singleton pattern must be used with care in multi-threaded situations. If two threads call to create an instance of the singleton class at the same time when the unique instance has not been created, then they do not detect the existence of the unique instance at the same time, so they each create an instance at the same time (for a single thread, creating an instance When it conforms to the characteristics of the singleton pattern), this may create two instances, thus violating the principle of instance uniqueness in the singleton pattern. The solution to this problem is to provide a mutex for whether the target class is already an instance (of course this will reduce efficiency).

 1. You cannot use the reflection mode to create a singleton, otherwise a new object will be instantiated. 
 2. Pay attention to thread safety when using the lazy singleton mode. 
 3. The construction methods of the hungry singleton mode and the lazy singleton mode are private. Therefore, it cannot be inherited, and some singleton patterns can be inherited (such as registration patterns)

3. Detailed explanation of the implementation of the singleton pattern

3.1. Example of the implementation process of the singleton pattern

Step 1: Create a Singleton class

/**   
 * Copyright © 2018 GBOYS CSDN Blog Module. All rights reserved.
 *
 * @Package: com.gxboys.pattern.singleton
 */

package com.gxboys.pattern.singleton;

/**
 * @See: Using GXBOYS Blog In CSDN
 *
 * @ClassName: Singleton
 * @Description: Singleton mode (process test)
 * @author: GXBOYS
 * @date: May 5, 2018 6:29:31 PM
 */

public class Singleton {

    // Create a singleton Object
    private static Singleton instance = new Singleton();

    // Private constructor, the object cannot be instantiated externally through the new keyword
    private Singleton() {
    }

    // Provide a unique interface method that can obtain the object
    public static Singleton getInstance() {
        return instance ;
    }

    // test method
    public void showMessage() { System.out  .println ( "Welcome to learn the singleton pattern process." );     } }
      

Step 2: Get the object from outside the Singleton (the Test class call)

package com.gxboys.pattern.singleton;

public class Test {

    public static void main(String[] args) {

        Singleton singleton = null;

        // 使用new关键字 编译时会报错,提示:The constructor Singleton() is not visible.
        // singleton = new Singleton();

        // 获取唯一可用的对象

        singleton = Singleton.getInstance();

        // 显示消息
        singleton.showMessage();
    }
}

第三步:测试结果

1、使用关键字new获取对象

2、调用方法获取对象


3.2、单例模式实现方式

单例模式实现方式一般分为懒汉模式、饿汉模式

第一种实现方式:懒汉模式,线程不安全

  • 是否Lazy初始化:
  • 是否多线程安全:否
  • 实现难易程度:容易
  • 描述:这种方式是最基本最简答的实现方式,但是最大的问题在于不支持多线程实现(即线程不安全)。因为没有设置加锁synchronize,严格意义上该方式不属于单例模式。这种方式lazy loading很明显,不要求线程安全,所以在多线程下是无法正常工作的。

代码实例

/**   
 * Copyright © 2018 GBOYS CSDN Blog Module. All rights reserved.
 *
 * @Package: com.gxboys.pattern.singleton
 */

package com.gxboys.pattern.singleton;

/**
 * @See: Using GXBOYS Blog In CSDN
 *
 * @ClassName: LazySingleton
 * @Description: 懒汉模式,线程不安全
 * @author: GXBOYS
 * @date: 2018年5月5日 下午7:10:38
 */

public class LazySingleton {
    // 设置实例变量
    private static LazySingleton instance;

    // 私有化构造器
    private LazySingleton() {
    }

    // 对外提供获取对象实例接口
    public static LazySingleton getInstance() {
        // 懒汉模式,对象实例存在直接返回,不存在则内部实例化
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

第二种实现方式:懒汉模式,线程安全加锁synchronize

  • 是否Lazy初始化:
  • 是否支持多线程:
  • 实现难易程度:容易
  • 描述:这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。
  • 优点:第一次调用才初始化,避免内存浪费。
  • 缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。
    getInstance() 的性能对应用程序不是很关键(该方法使用不太频繁)。

代码实例

/**   
 * Copyright © 2018 GBOYS CSDN Blog Module. All rights reserved.
 *
 * @Package: com.gxboys.pattern.singleton
 */

package com.gxboys.pattern.singleton;

/**
 * @See: Using GXBOYS Blog In CSDN
 *
 * @ClassName: LazySingleton
 * @Description: 懒汉模式,线程安全
 * @author: GXBOYS
 * @date: 2018年5月5日 下午7:26:35
 */

public class LazySingleton {

    private static LazySingleton instance;

    private LazySingleton() {
    }

    // 加锁
    public static synchronized LazySingleton getInstance() {
        if (instance == null) {
            instance= new LazySingleton();
        }
        return instance;
    }
}
The third way to achieve: villain mode
  •     Whether to initialize Lazy: No
  •     Whether to support multi-threading: yes
  •     Ease of implementation: easy
  •     Description: This method is more commonly used. Every thread will generate an instance, so there is no thread safety problem. But it is in this way that too much garbage will be generated and the GC pressure will be increased.
  •     Advantages: There is no lock, and the execution efficiency will be improved.
  •     Disadvantages: The class is initialized when the class is loaded, which wastes memory.

      It avoids the synchronization problem of multiple threads based on the classloder mechanism. However, instance is instantiated when the class is loaded. Although there are many reasons for class loading, most of them call the getInstance method in the singleton mode, but it is not certain. There are other ways (or other static methods) to cause class loading. At this time, the initialization instance obviously does not achieve the effect of lazy loading.

code example

/**   
 * Copyright © 2018 GBOYS CSDN Blog Module. All rights reserved.
 *
 * @Package: com.gxboys.pattern.singleton
 */

package com.gxboys.pattern.singleton;

/**
 * @See: Using GXBOYS Blog In CSDN
 *
 * @ClassName: HungarySingleton
 * @Description: 恶汉模式
 * @author: GXBOYS
 * @date: 2018年5月5日 下午7:40:46
 */

public class HungarySingleton {


    private static HungarySingleton instance = new HungarySingleton();

    private HungarySingleton() {
    }

    public static HungarySingleton getInstance() {
        return instance;
    }
}
The fourth implementation method: double-checked lock/double-checked lock (DLC, double-checked locking)
  • JDK version: from JDK1.5
  • Lazy initialization: yes
  • Is it multi-thread safe: yes
  • Difficulty to implement: more complicated
  • Description: This method uses a double lock mechanism, which is safe and maintains high performance in multi-threaded situations.

    The performance of getInstance() is critical to the application.

code example

/**   
 * Copyright © 2018 GBOYS CSDN Blog Module. All rights reserved.
 *
 * @Package: com.gxboys.pattern.singleton
 */

package com.gxboys.pattern.singleton;

/**
 * @See: Using GXBOYS Blog In CSDN
 *
 * @ClassName: DLCSingleton
 * @Description: 双重说锁/双重校验锁模式
 * @author: GXBOYS
 * @date: 2018年5月5日 下午7:53:42
 */

public class DLCSingleton {
   
    private volatile static DLCSingleton instance;

    private DLCSingleton() {
    }

    public static DLCSingleton getSingleton() {
        if (instance== null) {
            synchronized (DLCSingleton.class) {
                if (instance== null) {
                    instance= new DLCSingleton();
                }
            }
        }
        return instance;
    }
}
Note: The DLC mode needs to use the keyword volatile to understand what this keyword needs to study the JVM-related reordering features. It will not be introduced here for the time being, and the DLC mode will be explained separately later. This also requires JDK5 or later (since JDK5 uses the new JSR-133 memory model specification, which enhances the semantics of volatile)

The fifth implementation method: registration / static inner class

  • Lazy initialization: yes
  • Is it multi-thread safe: yes
  • Difficulty to achieve: average
  • Description: This method can achieve the same effect as DLC, but the implementation is simple to use lazy initialization for static fields, which should be used instead of DLC mode.

这种方式只适静态域的情况,DLC方式可以使用于实例域需要加载时使用。该方式同样利用classLoader机制来保证初始化instance时只有一个线程,它跟第三种方式不同的是:第三种只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading的效果),而这种方式是Single类被加载了,instance不一定被初始化。因为SingletonHoller类没有被主动使用,只有通过显示调用getInstance方法时,才会显显式装载SingletonHolder类,从而实例化instance。想象一下如果实例化 instance 很消耗资源,所以想让它延迟加载,另外一方面,又不希望在 Singleton 类加载时就实例化,因为不能确保 Singleton 类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化 instance 显然是不合适的。这个时候,这种方式相比第 3 种方式就显得很合理。

代码实例

/**  
 * Copyright © 2018 GBOYS CSDN Blog Module. All rights reserved.
 *
 * @Package: com.gxboys.pattern.singleton
 */

package com.gxboys.pattern.singleton;

/**
 * @See: Using GXBOYS Blog In CSDN
 *
 * @ClassName: StaticSingleton
 * @Description: TODO
 * @author: 登记式/静态内部类
 * @date: 2018年5月5日 下午9:46:48
 */

public class StaticSingleton {


    private static class SingletonHolder {
        private static final StaticSingleton INSTANCE = new StaticSingleton();
    }

    private StaticSingleton() {
    }

    public static final StaticSingleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

最后,对于枚举类型的单例,使用的相对比较少,在本文就不在赘述,有兴趣的朋友可以参考https://www.cnblogs.com/cielosun/p/6596475.html的详细解读。

一般情况下的赘述:不建议使用第 1 种和第 2 种懒汉方式,建议使用第 3 种饿汉方式。只有在要明确实现 lazy loading 效果时,才会使用第 5 种登记方式。如果涉及到反序列化创建对象时,可以尝试使用枚举方式。如果有其他特殊的需求,可以考虑使用第 4 种双检锁方式。



Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325585722&siteId=291194637