There are several examples in the interview, and several examples in the interview.
"Do you know how many methods can be used to write the 'taobao' word in xiangxiangdou ?"
Does tangle Singleton mode have several useful methods? It is a bit useful. During interviews, I often choose one or more of the writing methods as the headers to examine the design patterns and coding styles, but it is also easy to extend to other questions. Here I will explain some of the frequently-used methods of monkey, but do not stick to them to remember "The style of fennel beans ". The greatest pleasure of programming is "know everything, control everything ".
It can be divided into four categories. The following describes their basic forms, variants, and features.
Full Chinese Model
Touhan is the singleton mode with the most variants. Starting from buhan, we gradually learned about the issues that need to be concerned when implementing the singleton mode through its variants.
Basic fooham
If you are full, you can eat it when you are hungry. So he will not initialize the singleton first, and then initialize it when using it for the first time, that is, "lazy loading ".
|
// Fooham // UnThreadSafe public class Singleton1 { private static Singleton1 singleton = null ; private Singleton1() { } public static Singleton1 getInstance() { if (singleton == null ) { singleton = new Singleton1(); } return singleton; } |
The core of the full Chinese model is lazy loading. The advantage is faster startup and Resource Saving. Initialization of a singleton is required until the instance is accessed for the first time. The disadvantage is that the writing is troublesome and the thread is insecure, the if statement has a race condition.
Writing is not a big problem. It is easy to read. Therefore, in a single-threaded environment, the best way for the monkey brother is to write the script. However, in a multi-threaded environment, basic customers are completely unavailable. The following variants are all trying to solve the problem of insufficient thread security.
Buhan-variant 1
The most rude violation is to use the synchronized keyword to modify the getInstance () method, so as to achieve absolute thread security.
// Fooham // ThreadSafe Public class Singleton1_1 { Private static Singleton1_1 singleton = null; Private Singleton1_1 (){ } Public synchronized static Singleton1_1 getInstance (){ If (singleton = null ){ Singleton = new Singleton1_1 (); } Return singleton; } } |
The advantage of variant 1 is that it is easy to write and absolutely thread-safe; the disadvantage is that concurrency performance is very poor, and in fact it completely degrades to serial. The single instance only needs to be initialized once, but even after initialization, the synchronized lock cannot be avoided, so that getInstance () completely becomes a serial operation. It is recommended for scenarios with no performance sensitivity.
Buhan-variant 2
Variant 2 is the "Notorious" DCL 1.0.
For the problem that the lock still cannot be avoided after the initialization of a single instance in variant 1, variant 2 sets another check layer on the outer layer of variant 1, and the check of the inner layer of synchronized, double Check Lock (DCL ).
// Fooham
// UnThreadSafe
Public class Singleton1_2 {
Private static Singleton1_2 singleton = null;
Private Singleton1_2 (){
}
Public static Singleton1_2 getInstance (){
// May get half object
If (singleton = null ){
Synchronized (Singleton1_2.class ){
If (singleton = null ){
Singleton = new Singleton1_2 ();
}
}
}
Return singleton;
}
}
The core of Variant 2 is DCL. It seems that Variant 2 has achieved the desired effect: lazy loading + thread security. Unfortunately, as mentioned in the comments, DCL is still thread insecure, and you may get "half an object" due to Command Re-sorting ". After reading variant 3 in detail, you can refer to an article earlier by the monkey. I will not go into details here.
Reference: Role and principle of volatile keywords
Buhan-variant 3
Variant 3 specifically targets variant 2, which can be described as DCL 2.0.
For the "half object" problem of variant 3, variant 3 adds the volatile keyword to the instance. For the principle, see the above reference.
// Fooham
// ThreadSafe
Public class Singleton1_3 {
Private static volatile Singleton1_3 singleton = null;
Private Singleton1_3 (){
}
Public static Singleton1_3 getInstance (){
If (singleton = null ){
Synchronized (Singleton1_3.class ){
// Must be a complete instance
If (singleton = null ){
Singleton = new Singleton1_3 ();
}
}
}
Return singleton;
}
}
In a multi-threaded environment, variant 3 is more suitable for performance-sensitive scenarios. However, we will learn later that, even if the thread is secure, there are still some ways to destroy the Singleton.
Hunger Mode
Compared with the full Han, the hungry Han is very hungry and only wants to eat it as soon as possible. So he initialized the singleton at the earliest time, that is, when the class was loaded, and then returned directly when accessing the instance.
// Hungry Man
// ThreadSafe
Public class Singleton2 {
Private static final Singleton2 singleton = new Singleton2 ();
Private Singleton2 (){
}
Public static Singleton2 getInstance (){
Return singleton;
}
}
Ele. Me's advantage is its inherent thread security (thanks to the class loading mechanism), which is super simple to write and has no latency during use; the disadvantage is that it may cause a waste of resources (if the class is never used as a singleton after being loaded ).
It is worth noting that in a single-threaded environment, ELE. Me and ELE. Me have no difference in performance. But in a multi-threaded environment, ELE. Me has better performance because they need to be locked.
Holder Mode
We hope to utilize the convenience and thread security of static variables in the hungry Chinese mode, and to avoid resource waste through lazy loading. The Holder mode meets these two requirements: the core is still static variables, which is convenient and thread-safe. The static Holder class holds real instances and indirectly implements lazy loading.
|
// Holder Mode // ThreadSafe Public class Singleton3 { Private static class SingletonHolder { Private static final Singleton3 singleton = new Singleton3 (); Private SingletonHolder (){ } } Private Singleton3 (){ } Public synchronized static Singleton3 getInstance (){ Return SingletonHolder. singleton; } } |
Compared with the hungry Chinese mode, the Holder mode only adds the cost of a static internal class, which is equivalent to the effect (slightly better) of the full Chinese variant 3, and is a popular implementation method. We also recommend that you consider this.
Enumeration Mode
The Singleton mode with enumeration is quite easy to use, but its readability does not exist.
Basic Enumeration
Use the enumerated static member variables as a singleton instance:
|
// Enumeration // ThreadSafe public enum Singleton4 { SINGLETON; } |
The amount of code is less than that in the hungry Chinese mode. However, you can only directly access the instance Singleton4.SINGLETON-in fact, this access method is also appropriate as a Singleton, but sacrifices the advantages of the static factory method, such as the failure to implement lazy loading.
Ugly but easy-to-use syntactic sugar
Java enumeration is an ugly but easy-to-use syntactic sugar ".
Nature of enumeration Singleton Mode
Through decompilation (jad, source code | String concatenation operation "+" optimization? You can see the nature of enumeration types by opening the syntax sugar, which is simplified as follows:
|
// Enumeration // ThreadSafe Public class Singleton4 extends Enum <Singleton4> { ... Public static final Singleton4 SINGLETON = new Singleton4 (); ... |
Essentially the same as the hungry Chinese mode, the difference lies only in the public static member variables.
Use enumeration to implement trick
This part has nothing to do with the singleton. You can skip it. If you choose to read, you should also recognize the fact that although enumeration is quite flexible, it is difficult to use enumeration properly. A typical example is the TimeUnit class. It is recommended that you have time to read it.
As we can see above, the nature of an enumerative Singleton is still a common class. In fact, we can add any functions that can be completed by common classes on the enumeration type Singleton. The main point is the initialization of the enumeration instance, which can be understood as instantiating an anonymous internal class. To make it more obvious, we define a common private member variable, a common public member method, and a public abstract member method in Singleton4_1, as follows:
// Enumeration
// ThreadSafe
Public enum Singleton4_1 {
SINGLETON ("enum is the easiest singleton pattern, but not the most readable "){
Public void testAbsMethod (){
Print ();
System. out. println ("enum is uugly, but so flexible to make lots of trick ");
}
};
Private String comment = null;
Singleton4_1 (String comment ){
This. comment = comment;
}
Public void print (){
System. out. println ("comment =" + comment );
}
Abstract public void testAbsMethod ();
Public static Singleton4_1 getInstance (){
Return SINGLETON;
}
}
In this way, each enumeration instance in Singleton4_1 of the enumeration class not only inherits the print () member method of Singleton4_1 of the parent class, but also the abstract member method testAbsMethod () of the parent class Singleton4_1 ().
Summary
The above analysis ignores the reflection and serialization issues. Through reflection or serialization, we can still access the private constructor and create a new instance to destroy the singleton mode. In this case, only the enumeration mode can prevent this problem. The reflection and serialization monkey is not familiar yet, but the basic principle is not difficult. It can be implemented manually in other modes.
Next we will continue to ignore the reflection and serialization issues, so let's make a summary:
The Singleton mode is a frequent test site in the interview and is easy to write. On the one hand, check the correctness and analyze this article. On the other hand, check the coding style. For details, refer to the basic rules that programmers should remember.
Long press to identify QR code follow