// </summary> public class Config {private static Config _config = null; public static Config GetConfig() { if (_config == null) { _config = new Config(); } return _config; }}Copy the code
The singleton pattern
A singleton is an entire program with only one instance. This class is responsible for creating its own objects and ensuring that only one object is created.
Most programmers are asked to name three common design patterns, and singletons are likely to be one of them. The singleton mode refers to that there is only one instance of a certain type in a process. In fact, when extended to the concept of cluster, there can also be singleton between multiple processes in different physical environments. For example, the distributed lock used to blow water can be regarded as the singleton mode between multiple processes.
Through the singleton phenomenon, we can see that the singleton pattern is also to solve the problem of resource competition in essence. It makes multiple threads or even multiple processes share the same resource to achieve the purpose of resource sharing. Why implement resource sharing? This resource can only have one instance in business scenarios, such as the global configuration information above. If there are multiple instances of configuration information inside the program, the server memory resources are wasted. When the configuration information is modified, it is a big problem that multiple instances are updated synchronously.
The singleton implementation
The singleton pattern is simple in concept and can be implemented in many ways, but the focus is on the following:
- The main purpose of the constructor for private is to avoid external instance creation through New
- Whether the singleton supports lazy loading and whether the performance is efficient
- Whether object creation is safe in multi-threaded environment
- There is only one access point globally
In order to achieve the above requirements, we can have a number of implementation methods
The hungry type
public class Config { private Config() { } private static Config _config = new Config (); public static Config GetConfig() { return _config; }}Copy the code
This approach takes advantage of language features, where static properties of a type are owned by the type and loaded only once during the class’s lifetime, so the code above implements the singleton pattern with no problem and is super simple.
A lot of people say that this is a bad way to create an instance when the type is loaded, rather than lazy loading, resulting in a waste of memory. I don’t agree with you completely on this question. If the initialization of a singleton takes a long time, it is best not to wait until the actual use of the singleton to perform the initialization, which will affect the system performance. Hungry han type can realize initialized when starting operations, so it can avoid the initialization time is too long lead to performance problems, but also a comparatively important benefits, if there are mistakes initialization program, we can start the program when they found that rather than wait until program running exposed. This is similar to how compile-time errors are always easier to detect than run-time errors.
LanHanShi
If (_config == null) is not thread safe. If (_config == null) is not thread safe. If (_config == null) is not thread safe. If you want to change it, you can add a global lock mechanism. Note that the lock object must be static
private static object objLock = new object();
private static Config _config = null;
public static Config GetConfig()
{
lock (objLock)
{
if (_config == null)
{
_config = new Config();
}
}
return _config;
}
Copy the code
Double locking mechanism
Although the lazy way can ensure thread safety, but the lack of lock mechanism greatly reduces the performance of the system, the reason is that the lock mechanism to all requests sequentially, in order to improve the performance of the lazy, so the double lock mechanism appeared, in the context of ensuring thread safety, greatly improve the performance of the program
private static object objLock = new object();
private static Config _config = null;
public static Config GetConfig()
{
if (_config == null)
{
lock (objLock)
{
if (_config == null)
{
_config = new Config();
}
}
}
return _config;
}
Copy the code
These are just a few common ways to implement singletons. Depending on the characteristics of each language, there are many other ways to implement singletons, such as using c# or Java inner classes, static initialization features, etc.
Singleton pattern defect
- Object-oriented design emphasizes encapsulation, inheritance, polymorphism, and abstraction. Singleton pattern for its inheritance, polymorphic support is not good, abstraction is oriented to interface programming, singleton pattern has no interface concept. Taking the above configuration file singleton as an example, assuming that the configuration information is now loaded as a local file, the singleton pattern would have to modify the existing code if the requirement to load configuration information from the database was added later, which would violate the design rules to some extent. So singletons to some extent lose the expansibility to deal with future requirements.
- The singleton pattern is sometimes too responsible for the initialization process, the initialization content, and in some cases, other programs, which violates the “single responsibility” principle to some extent.
- Because the singleton is not explicitly initialized with constructor arguments as an entry point, it is not immediately clear which types are used internally, making it difficult for developers to identify class dependencies
- The singleton pattern is not suitable for scenarios where the surface is singleton but may be extended in the future. For example, thread pools are designed in a singleton pattern in many applications. Many developers assume that only one thread pool exists in an application, but there are scenarios where the same application may require multiple thread pools for individual needs.
Write in the last
The singleton pattern is the most commonly used pattern and has its own advantages and application scenarios. What if a type requires a certain number of instantiations in a program? For example, if a type can be instantiated up to 10 times, or one per thread, you might want to look into threadLocal or HashMap. As for the singleton realization between clusters, welcome to reflect in the message area!!
More interesting articles
- Distributed large concurrent series
- Architectural Design Series
- Series of interesting algorithms and data structures
- Design Pattern series