Single case pattern analysis

Singleton mode

Why single example

Ensure that a class has only one object, which is commonly used to access database operations, service configuration files, etc.

Key points of singleton

1. The default constructor is private, and the copy constructor and copy assignment function should also be disabled by private or = delete. (it cannot be constructed by other external objects)
2. Returns a singleton class object through a static method or enumeration.
3. Ensure that there is only one singleton class object in a multithreaded environment.

Several writing methods

This paper mainly introduces the lazy and hungry writing methods of C + +.

Lazy style

Only when a unique object needs to be generated (when calling GetInstance)

Thread unsafe error writing

class SingleInstance
{
public:
    // Get singleton by static method
    static SingleInstance *GetInstance();
    // Release singleton to avoid memory leakage
    static void deleteInstance();
private:
    SingleInstance() {}
    ~SingleInstance() {}
    // The copy constructor and copy assignment function are set to private and disabled
    SingleInstance(const SingleInstance &signal);
    const SingleInstance &operator=(const SingleInstance &signal);
private:
    static SingleInstance *m_SingleInstance;
};

// Initialize to NULL, in contrast to the following
SingleInstance *SingleInstance::m_SingleInstance = NULL;

SingleInstance* SingleInstance::GetInstance()
{
    // In the case of multithreading, when one thread passes the if check but the new singleton has not yet been issued, the other thread also passes the if check, resulting in new issuing multiple objects
    if (m_SingleInstance == NULL)
    {
        m_SingleInstance = new (std::nothrow) SingleInstance;
    }
    return m_SingleInstance;
}

void SingleInstance::deleteInstance()
{
    if (m_SingleInstance)
    {
        delete m_SingleInstance;
        m_SingleInstance = NULL;
    }
}

Thread safe double check lock writing method

class SingleInstance
{
public:
    // Get singleton by static method
    static SingleInstance *GetInstance();
    // Release singleton to avoid memory leakage
    static void deleteInstance();
private:
    SingleInstance() {}
    ~SingleInstance() {}
    // The copy constructor and copy assignment function are set to private and disabled
    SingleInstance(const SingleInstance &signal);
    const SingleInstance &operator=(const SingleInstance &signal);
private:
    static SingleInstance *m_SingleInstance;
};

// Initialize to NULL, in contrast to the following
SingleInstance *SingleInstance::m_SingleInstance = NULL;

SingleInstance* SingleInstance::GetInstance()
{
    // If you lock it directly outside, the function is ok, but you need to add a lock every time you run to this place, which is a waste of resources
    // Lock the inner layer. There is a lock during initialization. After initialization, the outer if is false and returns directly to avoid locking
    if (m_SingleInstance == NULL) 
    {
        std::unique_lock<std::mutex> lock(m_Mutex); // Lock up
        if (m_SingleInstance == NULL)
        {
            m_SingleInstance = new (std::nothrow) SingleInstance;
        }
    }
    return m_SingleInstance;
}

void SingleInstance::deleteInstance()
{
    if (m_SingleInstance)
    {
        delete m_SingleInstance;
        m_SingleInstance = NULL;
    }
}

Thread safe writing of local static variables

recommend

class SingleInstance
{
public:
    // Get singleton by static method
    static SingleInstance *GetInstance();
private:
    SingleInstance() {}
    ~SingleInstance() {}
    // The copy constructor and copy assignment function are set to private and disabled
    SingleInstance(const SingleInstance &signal);
    const SingleInstance &operator=(const SingleInstance &signal);
};

SingleInstance& SingleInstance::GetInstance()
{
	// Local static variables (generally static variables in functions) allocate memory and initialize when used for the first time.
    static SingleInstance m_SingleInstance;
    return m_SingleInstance;
}

Hungry Han style

Before the process runs (the main function executes), it is created

Thread safe process pre run initialization writing method

class SingleInstance
{
public:
    // Get singleton by static method
    static SingleInstance *GetInstance();
private:
    SingleInstance() {}
    ~SingleInstance() {}
    // The copy constructor and copy assignment function are set to private and disabled
    SingleInstance(const SingleInstance &signal);
    const SingleInstance &operator=(const SingleInstance &signal);
private:
	static SingleInstance *m_SingleInstance;
};

SingleInstance* SingleInstance::GetInstance()
{
    return m_SingleInstance;
}

// Global variables, static variables of file fields and static member variables of classes allocate memory and initialize during static initialization before main execution
Singleton* Singleton::g_pSingleton = new (std::nothrow) Singleton;
int main()
{
	return 0;
}

Thread safe writing of class static member variables

class SingleInstance
{
public:
    // Get singleton by static method
    static SingleInstance *GetInstance();
    // Global variables, static variables of file fields and static member variables of classes allocate memory and initialize during static initialization before main execution.
    static SingleInstance m_SingleInstance;
private:
    SingleInstance() {}
    ~SingleInstance() {}
    // The copy constructor and copy assignment function are set to private and disabled
    SingleInstance(const SingleInstance &signal);
    const SingleInstance &operator=(const SingleInstance &signal);
};

SingleInstance& SingleInstance::GetInstance()
{
    return m_SingleInstance;
}

Static internal class writing

JAVA

/**
 * The static inner class implements the singleton pattern
 */
public class Singleton {
    private Singleton() {
    }

    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }

    /**
     * Static inner class
     */
    private static class SingletonHolder {
        private static Singleton instance = new Singleton();
    }
}

The instance will not be initialized when the Singleton class is loaded for the first time. Only when the getInstance() method is called for the first time, the virtual opportunity loads the SingletonHolder class and initializes the instance.
This method not only ensures thread safety and the uniqueness of the singleton object, but also delays the initialization of the singleton. It is recommended to use this method to implement the singleton mode.

Enumeration singleton

JAVA

/**
 * Enumeration implements singleton mode
 */
public enum SingletonEnum {
    INSTANCE;
    public void doSomething() {
        System.out.println("do something");
    }
}

By default, the creation of enumeration instances is thread safe. Even deserialization will not generate new instances. In any case, it is a singleton.
Advantages: simple!

Container implementation singleton

JAVA

import java.util.HashMap;
import java.util.Map;
/**
 * The container class implements the singleton pattern
 */
public class SingletonManager {
    private static Map<String, Object> objMap = new HashMap<String, Object>();

    public static void regsiterService(String key, Object instance) {
        if (!objMap.containsKey(key)) {
            objMap.put(key, instance);
        }
    }

    public static Object getService(String key) {
        return objMap.get(key);
    }
}

Singleton manager can manage multiple singleton types. When used, get the object of the corresponding type according to the key. In this way, operations can be obtained through a unified interface, which hides the specific implementation and reduces the coupling degree.

reference resources

https://www.jianshu.com/p/e57098c265f9
https://www.cnblogs.com/kubixuesheng/p/4355055.html
https://stackoverflow.com/questions/1008019/c-singleton-design-pattern
https://programmer.ink/think/summary-of-c-thread-safety-singleton-patterns.html

Keywords: Java Programming Database

Added by wilburforce on Tue, 28 Sep 2021 20:31:49 +0300