Github source address
An overview of 23 design patterns
- Java Language Design – An overview of 23 design patterns
Creation pattern
- Factory Method Pattern
- Abstract Factory Pattern
- Builder Mode
- Prototype mode
- Singleton
Structural mode
- Facade Pattern
- Adapter mode (Adapter)
- Proxy mode
- Composite mode
- Flyweight Mode
- Decorator pattern
- Bridge mode (Bridge)
Behavioral pattern
- Mediator Mode
- Observer Model
- Command mode
- Iterator pattern (Iterator)
- Template Method
- Strategy Pattern
- State mode
- Memento Mode
- Interpreter mode
- Chain of Responsibility model
- Visitor Pattern
I. Mode introduction
Definition of schema
Ensure that there is only one instance of a class, and that you instantiate it yourself and make it available to the entire system.
Usage scenarios of patterns
Ensure that a class has only one object, such as creating an object that consumes too many resources, such as accessing IO and database resources.
Two.7 singleton pattern realization
There are many ways to implement singleton mode, mainly in the implementation of whether to support lazy mode, whether to use various skills in thread safety. Of course, there are some scenarios that don’t need to worry about lazy loading, or lazy mode, and instead use static static classes or properties and methods for external calls.
1. Lazy mode (thread unsafe)
public class SingletonLanHan {
private static SingletonLanHan singletonLanHan;
private SingletonLanHan(a) {}public static SingletonLanHan getInstance(a) {
if (singletonLanHan == null) { // The thread is not safe and may get two different instances
singletonLanHan = new SingletonLanHan();
}
returnsingletonLanHan; }}Copy the code
- One feature of the Singleton pattern is that it does not allow direct external creation, namely new Singleton(), so the private property private is added to the default constructor.
- The current singleton does satisfy lazy loading, but if multiple visitors are fetching object instances at the same time you can imagine a bunch of people trying to grab a toilet, there will be multiple instances of the same, which will not meet the requirements of the singleton.
2. Lazy mode (thread safety and low efficiency)
public class SingletonLanHan {
private static SingletonLanHan singletonLanHan;
private SingletonLanHan(a) {}public static synchronized SingletonLanHan getInstance(a) {
if (singletonLanHan == null) {
singletonLanHan = new SingletonLanHan();
}
returnsingletonLanHan; }}Copy the code
The getInstance() method adds the synchronized keyword, which means that getInstance() is a synchronized method
- Disadvantages: Inefficient, every thread that wants to get an instance of a class needs to execute the getInstance() method in synchronization. If you want to get an instance of the class, return it. Method synchronization efficiency is too low to improve.
3. Slacker mode – Dual checklock DCL(recommended for thread safety)
public class SingletonLanHan {
private static volatile SingletonLanHan singletonLanHan;
private SingletonLanHan(a) {}public static SingletonLanHan getInstance(a) {
if (singletonLanHan == null) {
synchronized (SingletonLanHan.class) {
if (singletonLanHan == null) {
singletonLanHan = newSingletonLanHan(); }}}returnsingletonLanHan; }}Copy the code
Why is double checklock implemented in singleton mode?
First check: the first if (singleton==null), this is to improve the code execution efficiency, because the singleton mode only needs to create the instance once, so when an instance is created, the call getInstance method does not need to enter the synchronized code block, do not need to compete for the lock. Simply return to the instance you created earlier.
Second check: If (singleton==null); if (singleton==null); if (singleton==null); Thread T1 is ready to continue, but since the resource is preempted by thread T2, page T2 calls getInstance. Likewise, since the singleton is not instantiated, T2 can pass the first if and proceed to synchronize the code block. The second if also passes. The T2 thread then creates an instance of singleton. At this point, the t2 thread completes its task, the resources return to t1 thread, and T1 enters the synchronized code block. If there is no second if, T1 will also create a singleton instance, and multiple instances will be created. You can completely avoid the problem of multiple instances being created by multiple threads.
So: both checks are essential
Also, private static volatile Singleton is essential
- The volatile keyword prevents JVM instruction reordering optimization
Singleton = new singleton () Allocate memory for singleton; 2. Initialize singleton; 3. Point the singleton to the allocated memory space. However, due to the reordering nature of the JVM, the order of execution can become 1-3-2. Instruction reordering in a single thread is not a problem, but in multithreading it causes a thread to acquire an uninitialized instance. For example, thread T1 executes 1 and 3, and T2 calls getInstance() to find that the Singleton is not empty, so it returns the Singleton, but the singleton has not yet been initialized. Using volatile prevents JVM instructions from being rearranged, thus ensuring proper execution across multiple threads.
4. Hungry Mode (thread-safe)
public class SingletonEHan {
private SingletonEHan(a) {}
private static final SingletonEHan singletonEHan = new SingletonEHan();
public static SingletonEHan getInstance(a) {
returnsingletonEHan; }}Copy the code
- Advantages: As we can see from its implementation, this approach is relatively simple to implement, complete the instantiation at the time of class loading, avoiding thread synchronization issues.
- Disadvantages: Since the class is instantiated when it is loaded, it is not Lazy Loading, which means that I may not use the instance, but it will load, causing a waste of memory (but this waste can be ignored, so it is recommended).
5. Inner classes (thread-safe)
public class SingletonIn {
private SingletonIn(a) {}private static class SingletonInHodler {
private static SingletonIn singletonIn = new SingletonIn();
}
public static SingletonIn getSingletonIn(a) {
returnSingletonInHodler.singletonIn; }}Copy the code
This approach is similar to, but different from, the hungrier approach. Both use a classloading mechanism to ensure that only one thread initializes instances.
Different places:
- In hangover mode, the Singleton class is instantiated as soon as it is loaded,
- The inner class loads the SingletonHolder class when it needs to be instantiated by calling the getInstance method
Advantages: Avoid thread insecurity, delay loading, high efficiency
6.CAS AtomicReference (Thread safety)
In computer science, Conmpare And Swap are the atomic instructions used to synchronize multiple threads. It compares the contents of a memory location to a given value, and only modifies the contents of that memory location to the new given value if they are the same. This is done as a single atom operation. Atomicity guarantees that the new value is calculated based on the latest information; If the value is updated by another thread at the same time, the write will fail. The operation result must indicate whether to replace; This can be done by a simple Boolean response (this variant is often called compare and set), or by returning a value read from a memory location.
public class SingletonCAS {
private static final AtomicReference<SingletonCAS> INSTANCE = new AtomicReference<SingletonCAS>();
private static SingletonCAS instance;
private SingletonCAS(a) {}public static final SingletonCAS getInstance(a) {
for(; ;) { SingletonCAS instance = INSTANCE.get();if (null! = instance)return instance;
INSTANCE.compareAndSet(null.new SingletonCAS());
returnINSTANCE.get(); }}}Copy the code
- The Java concurrency library provides many atomic subclasses to support data security for concurrent access; AtomicInteger, AtomicBoolean, AtomicLong, AtomicReference.
- AtomicReference can encapsulate references to a V instance, supporting concurrent access to the above singleton method is used to use such a feature.
- The advantage of using CAS is that there is no need to use traditional locking methods to ensure thread-safety, but rather rely on CAS busy algorithms, which rely on the implementation of the underlying hardware to ensure thread-safety. Compared to other lock implementations, there is no additional overhead of thread switching and blocking, and it can support greater concurrency.
- Of course, CAS also has a disadvantage that it is busy and so on. If it has not been obtained, it will be in an infinite loop.
7.Effective Java Author recommended enumeration singletons (thread-safe)
public enum SingletonEnum {
instance;
public void test(a) {}}Copy the code
Extremely simple to use
SingletonEnum.instance.test();
Copy the code
This approach is functionally similar to the common domain approach, but it is simpler, provides serialization for free, and absolutely prevents instantiation, even in the face of complex serialization or reflection attacks. Although this approach is not yet widely adopted, single-element enumerated types have emerged as the best way to implement Singleton.
But be aware that this approach is not available in inheritance scenarios.
3. To summarize
- Although it is a very common singleton pattern, you can really see the basic Java skills in various implementations, including here; Lazy, hungry, thread safety, static classes, inner classes, locking, serialization, etc.
- In normal development, if you can ensure that this class is globally available and does not need lazy loading, you can create it and call it externally. But if you have a lot of classes, some of which need to be displayed after the user triggers certain conditions (the game level), then lazy loading is a must. Thread safety can be selected on demand.
Code Address:
- Github:github.com/leifu1107/a…