The singleton pattern is the easiest to understand and the easiest way to hand-written code in the design pattern, but there are not many knowledge points involved, so it is often used as an interview question. Generally, singletons are written in five ways: lazy, hungry, double check lock, static internal classes and enumeration. In order to record the learning process, several common singleton writing methods have been compiled here.
Bronze 5: (Lazy-loaded, but thread is not safe)
When asked about implementing a singleton pattern, many people's first reaction is to write the following code, including the same teachings in textbooks.
public class Singleton { private static Singleton instance; private Singleton(){} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }}This code is simple and straightforward, and uses delayed loading mode, but threads are not safe. Calling the getInstance() method in a multi-threaded environment may cause multiple threads to enter the program code block of if statement.
Lazy style: synchronized (Lazy-loaded, thread-safe, but not efficient)
To solve the above problem, the easiest way is to set the entire getInstance() method to synchronized.
public class Singleton { private static Singleton instance; private Singleton() {} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }}Although it is thread-safe and delayed loading, it is not efficient. Because at any time there can only be one thread calling the getInstance() method. However, the synchronized operation only needs to be needed on the first call, that is, when the singleton instance object is created for the first time. This pattern causes only one thread to access the getInstance() method at a time even after the singleton is created, which can lead to potential performance problems. This leads to a double check lock.
Hungry style: static final field (non-Lazy-loaded)
This method is very simple, because the instance of a singleton is declared as static final and is initialized when the class is loaded into memory for the first time, so creating an instance object is thread-safe (guaranteed by the JVM implementation).
public class Singleton{ //Initialize private static final Singleton instance = new Singleton(); private Singleton(){} public static Singleton getInstance(){ // Singleton with static factory return instance; }}It is not a lazy loading mode, instance will be initialized from the beginning after the class is loaded, even if the client does not call the getInstance() method. This will lead to some usage restrictions: for example, the creation of a Singleton instance depends on parameters or configuration files. Before getInstance(), a certain method must be called to set parameters to it, so that this singleton writing method will not be used. Similar methods include:
public class Singleton{ public static final Singleton instance = new Singleton(); // Singleton with public final field private Singleton(){}}// <Effective Java> Page 14 tells the difference between the twoDouble-check lock + volatile (Lazyload, thread-safe, but obscure)
Double checked locking pattern is a method of locking using synchronous blocks. Programmers call it a double check lock because there will be two check instance == null, once outside the synchronization block and once inside the synchronization block. Why do we need to check again in the synchronization block? Because multiple threads may enter if outside the synchronization block together, if no secondary verification is performed in the synchronization block, multiple instance objects will be generated.
public static Singleton getSingleton() { if (instance == null) { //Single Checked synchronized (Singleton.class) { if (instance == null) { //Double Checked instance = new Singleton(); } } } return instance ;}This code looks perfect, but unfortunately it is problematic. The main thing is the sentence instance = new Singleton(). This is not an atomic operation. In fact, this sentence in JVM does roughly the following 3 things.
However, there is optimization of instruction reordering in JVM's JIT compiler. In other words, the order of the second and third steps above cannot be guaranteed, and the final execution order may be 1-2-3 or 1-3-2. If it is the latter, it will be preempted by thread 2 before execution of 3 and 2 is not executed. At this time, the instance is already non-null (but not initialized), so thread 2 will directly return the instance, then use it, and then naturally report an error. To do this, we need to declare the instance variable as volatile .
public class Singleton { private volatile static Singleton instance; //Declare as volatile private Singleton(){} public static Singleton getSingleton() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } return instance; } }However, it is important to note that there are still problems with the dual-check lock of volatile in versions before Java 1.5. This problem was only fixed in Java 1.5, so you can use volatile after that.
Static inner class: IoDH, initialization-on-demand holder
This pattern combines Java's static internal classes and multi-threaded default synchronization lock knowledge, and cleverly implements both latency loading and thread safety.
public class Singleton { private Singleton() {} private static class LazyHolder { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { // From wikipedia return LazyHolder.INSTANCE; }}A static inner class is equivalent to the static part of its outer class. Its objects do not depend on external class objects, so they can be created directly. Static internal classes will only be reproduced when they are used for the first time.
Multithreaded default synchronization lock
As we all know, in multi-threaded development, in order to solve the concurrency problem, it is mainly to use synchronized to add mutexes for synchronization control. But in some cases, the JVM already implicitly performs synchronization for you, and in these cases there is no need to manually perform synchronization control. These situations include :
1. When initializing data by a static initializer (initializer on a static field or in a static{} block)
2. When accessing the final field
3. When creating an object before creating a thread
4. When the thread can see the object it will process
Enum
Starting from Java 1.5, just write an enum type containing a single element:
public enum Singleton { INSTANCE;}This method is functionally similar to the public domain method, but it is more concise and provides a serialization mechanism free of charge, absolutely preventing multiple instantiation, even when facing complex serialization or reflection attacks. Although this approach has not been widely adopted, the enumeration types of single elements have become the best way to implement Singleton.
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1.What are the details of static final
2. Is the assignment initialization at the static field in sequence and the static code block?
3. How to write a singleton pattern for static internal classes
4. Does the examples in Java EE design pattern analysis and application really have a Lazyload effect?
The above is all the content of this article. I hope that the content of this article will be of some help to everyone’s study or work. I also hope to support Wulin.com more!