Design pattern
1. Create mode
Because the division of labor in software design is becoming more and more detailed, the separation of object creation and object use has become an inevitable trend. Because the creation of objects will consume a lot of resources of the system, it is a problem to study the creation of objects separately so as to create objects efficiently. Here are six specific creative models to study, which are:
Simple Factory mode
Factory Method
Abstract Factory pattern
Creator mode (Builder)
Prototype
Singleton mode
2. Structural mode
After solving the problem of object creation, the composition of objects and the dependencies between objects have become the focus of developers, because how to design the structure, inheritance and dependencies of objects will affect the maintenance of subsequent programs, code robustness, coupling and so on. The design of object structure can easily reflect the level of designers. Here are seven specific structural models for research, which are:
Appearance mode / Facade mode (Facade mode)
Adapter mode
Proxy mode (proxy)
Decorator
Bridge mode / bridge mode
Composite mode
Flyweight mode
3. Behavioral model
After the problems of object structure and object creation are solved, the problem of object behavior remains. If the behavior of objects is well designed, the behavior of objects will be clearer and the cooperation efficiency between them will be improved. Here are 11 specific behavior patterns to study, which are:
Template Method
Observer mode (observer)
State mode
Strategy
Chain of Responsibility
Command mode (command)
Visitor mode
Mediator mode
Memo mode (memo)
Iterator mode (iterator)
Interpreter mode (interpreter)
Singleton mode of design mode
Introduction:
A common software design pattern, which is defined as a class of singleton objects. Only one instance can exist,
The implementation of singleton is mainly through the following two steps: 1. Define the construction method of this class as a private method, so that the code in other places cannot instantiate the object of this class by calling the construction method of this class. 2
Only the static methods provided by this class can get the unique instance of this class;
2. Provide a static method in the class. When we call this method, if the reference held by the class is not empty, it will be returned. If the reference held by the class is empty, it will be created
An instance of the class and assign the reference of the instance to the reference maintained by the class
Application scenario:
bean object in spring
Multithreaded thread pool design, this is because the thread pool needs to facilitate the control of threads in the pool
Advantages and disadvantages of singleton mode:
Advantages: save memory space
Avoiding frequent creation and destruction of objects can improve performance
Avoid multiple occupation of shared resources and simplify access
Disadvantages:
Not applicable to frequently changing objects
Designing the database connection pool object as a singleton class may cause too many programs sharing the connection pool object to overflow the connection pool,
If the instantiated object is not used for a long time, the system will consider the object as garbage and be recycled, which may lead to the loss of object state
Implementation of singleton mode:
Hungry Han style
// Hungry Han style single case public class Singleton { // Private static reference pointing to your own instance, actively created private static Singleton singleton = new Singleton(); // Private construction method private Singleton(){} // Static public method and static factory method with their own instance as the return value public static Singleton getSingleton(){ return singleton; } }
As we know, classes are loaded on demand and once.. Therefore, when the above singleton class is loaded, an object will be instantiated and handed over to its own reference for use by the system; Moreover, since this class will only be loaded once in the whole life cycle, only one instance will be created, which can fully guarantee the singleton.
Advantages: this method is relatively simple, that is, instantiation is completed when the class is loaded. Thread synchronization problems are avoided.
Disadvantages: the instantiation is completed when the class is loaded, which does not achieve the effect of Lazy Loading. If this instance is never used from beginning to end, it will cause a waste of memory
Lazy single case
// Lazy single case public class Singleton { // Private static reference to own instance private static Singleton singleton; // Private construction method private Singleton(){} // Static public method and static factory method with their own instance as the return value public static Singleton getSingleton(){ // Create passively, only when you really need to use it if (singleton == null) { singleton = new Singleton(); } return singleton; } }
We can see from the lazy singleton that the singleton instance is loaded late, that is, an object will be instantiated and handed over to its own reference only when it is actually used.
This writing method has the effect of Lazy Loading, but it can only be used under single thread. If, under multithreading, one thread enters the if (singleton == null) judgment statement block and has not yet had time to execute, and another thread also passes the judgment statement, multiple instances will be generated. Therefore, this method cannot be used in a multithreaded environment.
Lazy thread safety:
To solve the above problem, the simplest way is to set the entire getInstance() method to synchronized.
public class Singleton { private static Singleton singleton; private Singleton (){} public static synchronized Singleton getSingleton() { if (singleton == null) { singleton = new Singleton(); } return singleton; } }
Lazy: double check lock
double checked locking pattern is a method of locking using synchronous blocks. Programmers call it a double check lock, because instance == null will be checked twice, once outside the synchronization block and once inside the synchronization block. Why do I have to check again in the synchronization block? Because multiple threads may enter the if outside the synchronization block together, multiple instances will be generated if no secondary verification is performed in the synchronization block.
public static Singleton getSingleton() { if (singleton == null) { //Single Checked synchronized (Singleton.class) { if (singleton == null) { //Double Checked singleton = new Singleton(); } } } return singleton ; }
This code looks perfect. Unfortunately, it has a problem. It mainly lies in the sentence singleton= new Singleton(), which is not an atomic operation. In fact, this sentence does the following three things in the JVM.
1 allocate memory to singleton
2 call the constructor of Singleton to initialize the member variable
3 point the singleton object to the allocated memory space (after this step, the singleton will be non null)
However, there is an optimization of instruction reordering in the JVM's just in time compiler.
In other words, the sequence of the second and third steps above cannot be guaranteed, and the final execution sequence may be 1-2-3 or 1-3-2. If it is the latter, it is preempted by thread 2 before 3 is executed and 2 is not executed. At this time, the singleton is already non null (but not initialized), so thread 2 will directly return the singleton, use it, and then report an error logically.
To explain it a little more, because there is an intermediate state of "singleton is not null but has not completed initialization", and at this time, if other threads just run to the first layer if (singleton==null), the singleton read here is no longer null, so you can directly use the singleton in this intermediate state, There will be problems. The key here is that thread T1 does not complete the write operation to singleton, and thread T2 performs the read operation.
Lazy: instruction reordering
We just need to declare the instance variable volatile.
public class Singleton { private volatile static Singleton singleton; //Declare volatile private Singleton (){} public static Singleton getSingleton() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }
The main reason for using volatile is another feature: Prohibition of instruction reordering optimization.
How to prevent reflection, cloning and serialization from breaking the singleton pattern
1. Prevent reflection from breaking the ring (although the constructor has been privatized, it can also be called by using the newInstance() method through the reflection mechanism)
1.1 to solve reflection corruption, you can add judgment conditions in the constructor to judge whether the instance is empty and throw an exception if it is not empty
if (singleton != null) { throw new RuntimeException("How dare you break my case....."); }
Using counting in the constructor of singleton mode ensures that only one object is produced, because reflection also calls the constructor to create the object.
/** * Singleton mode of double detection lock */ class DoubleIfSynchronizedSingleton { private static int count = 0; private volatile static DoubleIfSynchronizedSingleton singleton = null; private DoubleIfSynchronizedSingleton() { synchronized (DoubleIfSynchronizedSingleton.class) { if (count > 0){ throw new RuntimeException("How dare you break my case....."); } count++; } } public static DoubleIfSynchronizedSingleton getSingleton() { if (singleton == null) { synchronized (DoubleIfSynchronizedSingleton.class) { if (singleton == null) { singleton = new DoubleIfSynchronizedSingleton(); } } } return singleton; } }
2. Prevent clone breakage
-
Override clone() to return the singleton object directly
@Override protected Singleton clone() throws CloneNotSupportedException { return singleton; }
3. Prevent ring breakage
-
Add readResolve() to return the Object object
private Object readResolve() { return singleton; }
static nested class
I prefer to use the method of static inner class, which is also recommended in Effective Java.
public class Singleton { private static class SingletonHolder { private static final Singleton singleton = new Singleton(); } private Singleton (){} public static final Singleton getSingleton() { return SingletonHolder.singleton; } }
This method still uses the JVM's own mechanism to ensure thread safety; SingletonHolder is private, and there is no way to access it except getInstance(), so it is lazy; At the same time, the instance will not be synchronized when it is read, and there is no performance defect; It also does not depend on the JDK version.
Can't prevent reflection,
Enum enum
It's too simple to write a single example with enumeration! This is also its biggest advantage. The following code is the usual way to declare enumerated instances.
public enum EasySingleton{ SINGLETON; }
Thread safe, so you don't need to worry about double checked locking
Prevent serialization,
Prevent reflection
We can access the instance through EasySingleton.SINGLETON, which is much simpler than calling the getSingleton() method.
epilogue
Of course, there are many ways to implement singleton mode. From these implementations, we can conclude that in order to achieve efficient thread safe singleton, we must pay attention to the following two points:
- Minimize the scope of synchronization blocks;
- Try to use fine-grained locks.