Hello, everyone, I am the class representative.
attention to my public account: 16125c0542a5d2 Java class representative , get more java practical dry goods.
0. Preface
If you ask a programmer who has written code for a few years what design patterns have been used, I bet that more than 90% of the answers will include [singleton pattern]. Some interviewers will even ask directly: Tell me which design patterns you have used, so there is no need to talk about singletons. You see, even the interviewer is annoyed by it, and the popularity is evident.
However, the seemingly simple singleton pattern contains a lot of Java
foundations. In the daily development process, class representatives have seen many irregular and even problematic singleton implementations. So organize this article and summarize the best practices of singleton mode.
1. Lazy loading (lazy man)
The so-called lazy loading means that it is not loaded until the first time it is called. Its implementation needs to consider concurrency issues and instruction rearrangement, the code is as follows:
public class Singleton {
private volatile static Singleton instance; //①
private Singleton() { //②
}
public static Singleton getInstance() {
if (instance == null) {//③
synchronized (Singleton.class) {
if (instance == null) {//④
instance = new Singleton();//⑤
}
}
}
return instance;
}
}
This code is extremely concise, not a single character is superfluous, let's interpret it line by line:
First, notice the volatile
keyword at ①, which has two characteristics:
One is to ensure the visibility of this variable to all threads. That is, when a thread modifies the value of this variable, the new value is immediately known to other threads.
The second is to prohibit instruction reordering optimization.
Here is an explanation of instruction reordering optimization:
instance = new Singleton();
at code ⑤ is not atomic, and can be roughly divided into the following three steps:
- Allocate memory
- Call the constructor to initialize into an instance
- Let
instance
point to the allocated memory space
The JVM allows instruction reordering optimization under the premise of ensuring the correct result. That is, the possible sequence of the above 3 steps is 1->2->3 or 1->3->2. If the order is 1->3->2, when 3 is executed and 2 is not yet executed, another thread executes to code ③, and finds that instance
not null
, and directly returns the uninitialized instance
and uses it. Report an error.
Therefore, volatile
is used to ensure visibility between threads and prevent instruction rearrangement.
Secondly, the purpose of declaring the constructor as private
in code ② is to prevent the use of new Singleton()
like 06125c0542a94d to generate new instances.
Finally, when the client calls Singleton.getInstance()
, it first checks whether it has been instantiated (code ③), synchronizes the code block if it is not, and then checks whether it has been instantiated (code ④) again, and then executes code ⑤. The significance of the two checks is to prevent other threads from being instantiated during the synchronization of synchronized
This is the famous double check lock (Double check lock) to achieve singleton, that is, lazy loading.
TIPS:
There is also a
getInstance()
method on the Internet. Such a wide range of method level locks will lead to lower concurrency. In fact, after the first call to generate an instance, subsequent acquisitions of the instance do not require concurrency control at all. The double-checked lock version of this example can avoid this concurrency problem.
2. Preload (hungry man)
Corresponding to lazy loading, pre-loading is initialized when the class is loaded, so it is naturally thread-safe. The code is as follows:
public class Singleton {
private static final Singleton instance = new Singleton();// ①
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
Note that the class variable at ① uses final
.
The final
here is to provide grammatical constraints. After all, you are a singleton, there is only one instance, and it is impossible to point to another. instance
final
the constraint of 06125c0542aab9. If someone accidentally writes and modifies the code pointed to, it will report a syntax error.
This is like the @Override
annotation. You can guarantee to write the method name and parameters correctly. It is no problem if you don't write the annotation. However, with the constraint of the annotation, the compiler will check it for you and prevent others from changing it.
3. Static inner class
This method is the same as the pre-loading principle, and both use the characteristics of JVM class loading to achieve natural thread safety. The difference is that the static inner class achieves lazy loading.
public class Singleton {
private static class SingletonHolder {
private static Singleton instance = new Singleton();
}
private Singleton(){}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
SingletonHolder
is a static internal class. When the external class Singleton
is loaded, no instance will be created. Only when Singleton.getInstance()
is called will the Singleton
instance be created. All this is done naturally by the JVM, so it not only ensures thread safety, but also Realized lazy loading.
4. Enumeration
Yes, enumerations can implement singletons, and this method is the recommended implementation method in the second edition of "Effective Java Chinese Edition". The code is extremely simple:
public enum Singleton {
/**
* 单例实例
*/
INSTANCE;
public void doSomeThing(){
System.out.println("done");
}
}
When using it, you can Singleton.INSTANCE.doSomeThing();
The following two features of enumeration are mainly used here:
- Enumeration constructors are always private, so there is no need to explicitly define private constructors like the previous methods
- Each value in the enumeration class is an instance (there is only one instance of
INSTANCE
In addition, enumeration also comes with some additional benefits: it provides a serialization mechanism free of charge, and it can also prevent multiple instances from being generated through multiple deserialization.
In view of this, the best practice for singletons is to use enumerations.
5. Summary
In fact, the writing of singleton is not limited to the four mentioned in this article. You may also see many other variants, which more or less have some defects. For example, the lazy loading method applies synchronized
to the entire method. It can also be achieved, but frequent locking and releasing locks will cause performance bottlenecks, and completely removing the locks will bring concurrency problems.
Therefore, as long as you thoroughly understand the four singleton methods listed in the article, you can draw inferences from one another, and you can see right and wrong at a glance when you see a singleton written by others.
The four singleton modes listed in the article, except for enumeration, all use the static
keyword. The "Java Virtual Machine Specification" stipulates that there are several situations where the class must be "initialized" static
. The scenarios involving 06125c0542ad74 are as follows :
When reading or setting a static field of a type (except for the static field modified by final, the result has been put into the constant pool at compile time).
When calling a static method of a type.
Lazy loading, preloading and static inner classes take advantage of these two characteristics.
For static
, please refer to my other article: "One Question for Static Keywords"
Finally, once again, if you need to write a singleton in your development, I suggest you follow Joshua Bloch's advice in the second edition of "Effective Java Chinese Edition":
The single-element enumeration type has become the best way to implement Singleton
Reference materials:
1. "Effective Java Chinese Edition" Joshua Bloch Second Edition P15
2. "In-Depth Understanding of Java Virtual Machine" Zhou Zhiming 3rd Edition, P444-P448, P264
3. single-instance SINGLETON design pattern
[Past original recommendation]
One question to get the static keyword
Use Spring Validation to validate parameters
downloaded attachment name is always garbled? You should read the RFC document!
The codeword is not easy, welcome to like, follow and share.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。