java design pattern - singleton pattern
1, Overview
The definition of singleton pattern is to ensure that there is only one instance of a class and provide a global access point. It belongs to the creative pattern in the three categories of design patterns.
The singleton mode has three typical characteristics:
There is only one instance.
Self instantiation.
Provides a global access point.
2, Advantages and disadvantages
Advantages: since the singleton mode generates only one instance, it can save system resources, reduce performance overhead, improve system efficiency, and strictly control customers' access to it.
Disadvantages: because there is only one instance in the system, the responsibility of the singleton class is too heavy, which violates the "single responsibility principle", and there is no abstract class, so it is difficult to expand.
3, Common implementation methods
There are five common implementation methods of singleton mode: hungry, lazy, double detection lock, static internal class and enumeration singleton. Among these five ways, hungry and lazy are the most common. The implementation methods of these five methods are listed below:
-
Hungry Chinese style: thread safety and high calling efficiency. However, you cannot delay loading. Example:
public class HungryMode { /** * Thread safe * Class is initialized, this object is loaded immediately */ private static HungryMode hungry = new HungryMode(); private HungryMode() { } /** * Method does not add synchronization block, so it is efficient * @return */ public static HungryMode getInstance() { return hungry; } }
Because the object is created when the class is loaded in this mode, the speed of loading the class is relatively slow, but the speed of obtaining the object is relatively fast and thread safe.
-
Lazy: thread unsafe. Example:
public class Lazymode { /** * Thread unsafe */ private static Lazymode lazymode = null; private Lazymode() { } /** * Load objects at runtime * * @return */ public static Lazymode getInstance() { if (lazymode == null) { lazymode = new Lazymode(); } return lazymode; } }
Because this mode loads objects at runtime, it is faster to load classes, but the object acquisition speed is relatively slow and the thread is unsafe. If you want thread safety, you can add the synchronized keyword, but it will pay a heavy cost of efficiency.
- Lazy type (double synchronous lock)
public class LazyModeSync { private static volatile LazyModeSync instance = null; private LazyModeSync() { } /** * Load objects at runtime * @return */ public static LazyModeSync getInstance() { if (instance == null) { synchronized (LazyModeSync.class) { if (instance == null) { instance = new LazyModeSync(); } } } return instance; } }
- Dual detection lock supplement:
Why is it necessary to judge the space again after adding the synchronization lock?
Because if there is no second air judgment, the following situations may occur:
Thread 1 | Thread 2 |
---|---|
instance is determined to be empty for the first time | instance is determined to be empty for the first time |
Acquire lock | Wait 1 to release the lock |
Initialize instance | - |
Obtain the lock and initialize instance |
In this way, instance will be initialized twice, so it is necessary to perform secondary null determination after obtaining the lock.
6. Why use volatile keyword?
Because there may be instruction rearrangement during java initialization
Instruction rearrangement: Generally speaking, initialization is not an atomic operation, but is divided into three steps: Open up the space required for objects in the heap and allocate addresses Initialize according to the initialization order of class loading Returns the memory address to the reference variable in the stack because Java The memory model allows "write out of order". Some compilers may reorder 2 and 3 in the above steps for performance reasons Open up the space required for objects in the heap and allocate addresses Return the memory address to the reference variable in the stack (at this time, the variable is no longer null,But variables are not initialized) Initialize according to the initialization order of class loading
Thread 1 | Thread 2 |
---|---|
For the first detection, instance is empty | |
Acquire lock | |
Detect again, and instance is empty | - |
Allocate memory space in the heap | |
instance points to the allocated memory space | |
For the first detection, instance is not empty | |
Access instance (at this time, the object is still initialized) |
After adding volatile keyword modification, instruction rearrangement will be disabled, so as to ensure thread synchronization.
4, Common application scenarios
- Site counters.
- The class in the project used to read the configuration file.
- Database connection pool. Because the database connection pool is a database resource.
- In Spring, each Bean is singleton by default, which is convenient for the Spring container to manage.
- Application in Servlet