Design Patterns (3): Singleton Pattern

The singleton pattern in java is a common design pattern. There are several ways to write the singleton pattern. Here are three main types: lazy singleton, hungry singleton (commonly used), and registered singleton.
  The singleton pattern has the following characteristics:
  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.

Intent: To ensure that there is only one instance of a class, and to provide a global access point to it.

The main solution: 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: Determine whether the system already has this singleton, if so, return it, if not, create it.

Key code: The constructor is private.

Application examples:  1. A party can only have one chairman. 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, so the processing of all files must be carried out through a unique instance. 3. Some device managers are often designed as a singleton mode. For example, a computer has two printers. When outputting, it is necessary to process that the two printers cannot print the same file.

Advantages:  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 home page cache of the School of Management). 2. Avoid multiple occupation of resources (such as file writing operations).

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

Usage scenarios:  1. It is required to produce a unique serial number. 2. The counter in the WEB does not need to be added to the database every time it is refreshed, but is cached first with a singleton. 3. An object created needs to consume too many resources, such as the connection between I/O and the database. In computer systems, the driver objects of thread pools, caches, log objects, dialog boxes, printers, and graphics cards are often designed as singletons. These applications all have the function of resource manager more or less. Each computer can have several printers, but only one Printer Spooler (spooler) to avoid two print jobs being output to the printer at the same time. Each computer can have several communication ports, and the system should manage these communication ports centrally to avoid a communication port being called by two requests at the same time. In summary, the singleton pattern was chosen to avoid inconsistent states.

Note: The getInstance() method needs to use a synchronization lock synchronized (Singleton.class) to prevent multiple threads from entering at the same time and causing instance to be instantiated multiple times.

Lazy Man: Thread Unsafe

Lazy initialization: yes

Is it multi-thread safe: no

Difficulty to achieve: easy

Description: This method is the most basic implementation. The biggest problem with this implementation is that it does not support multithreading. Because there is no lock synchronization, it is not strictly a singleton mode.
This method of lazy loading is obvious, does not require thread safety, and does not work well in multi-threading.

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
  
    public static Singleton getInstance() {  
    if (instance == null) {  
        instance = new Singleton();  
    }  
    return instance;  
    }  
}  

饿汉式:

public class Singleton {  
    private static Singleton instance = new Singleton();  
    private Singleton (){}  
    public static Singleton getInstance() {  
    return instance;  
    }  
} 

登记式单例:

是否 Lazy 初始化:

是否多线程安全:

实现难度:一般

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

public class Singleton {  
    private static class SingletonHolder {  
    private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
    return SingletonHolder.INSTANCE;  
    }  
}   

饿汉式和懒汉式区别

从名字上来说,饿汉和懒汉,

饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,

而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。

另外从以下两点再区分以下这两种方式:

1、线程安全:

饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题,

懒汉式本身是非线程安全的,为了实现线程安全有几种写法,分别是上面的1、2、3,这三种实现在资源加载和性能方面有些区别。

2、资源加载和性能:

饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成,

而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。

线程安全:

如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

或者说:一个类或者程序所提供的接口对于线程来说是原子操作,或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题,那就是线程安全的。


Guess you like

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