catalogue
1. Structure of singleton mode
2. Implementation of singleton mode
2.1 hungry Han style (static variable mode)
2.2 hungry Chinese style (static code block mode)
2.5. Lazy type (double check lock)
2.6. Lazy type (static internal type)
3.1.1 serialization and deserialization
3.2.1 solution to destroy the singleton mode by serialization and deserialization
3.2.2 solution of single case cracking in reflection mode
4. JDK source code parsing - Runtime class
Concept:
Singleton Pattern is one of the simplest design patterns in Java. This type of design pattern is a creation pattern, which provides the best way to create objects.
This pattern involves a single class that is responsible for creating its own objects while ensuring that only a single object is created.
This class provides a way to access its unique object, which can be accessed directly without instantiating the object of this class.
1. Structure of singleton mode
The main roles of singleton mode are as follows:
-
Singleton class. Only one instance of a class can be created
-
Access class. Using singleton classes
2. Implementation of singleton mode
There are two types of singleton design patterns:
Hungry Chinese style: class loading will cause the single instance object to be created
Lazy: class loading does not cause the single instance object to be created, but only when the object is used for the first time
2.1 hungry Han style (static variable mode)
package com.jie.creator.singleton.demo01; /** * @description:Hungry Chinese style: static variables create objects of classes * @author: jie * @time: 2022/1/29 14:43 */ public class Singleton { /** * @description:The constructor is privatized to avoid external object creation */ private Singleton() {} /** * @description:Create this class object in this class and modify it with static to ensure that it can be accessed by static methods */ private static Singleton instance = new Singleton(); /** * @description:External direct call static method instantiation object */ public static Singleton getInstance() { return instance; } }
package com.jie.creator.singleton.demo01; /** * @description:Test class * @author: jie * @time: 2022/1/29 14:44 */ public class Test { public static void main(String[] args) { //1. Create an object of the Singleton class Singleton singleton = Singleton.getInstance(); Singleton singleton1 = Singleton.getInstance(); //Judge whether the two obtained objects are the same object System.out.println(singleton==singleton1); } }
The result is true, indicating that singleton and singleton1 are the same memory address in the memory, so they are the same object.
This method declares the static variable of Singleton type at the member position, and creates the object instance of Singleton class. The instance object is created as the class loads. If the object is large enough and has not been used, it will cause a waste of memory.
2.2 hungry Chinese style (static code block mode)
package com.jie.creator.singleton.demo02; /** * @description:Hungry Chinese style: create this kind of object in a static code block * @author: jie * @time: 2022/1/29 15:59 */ public class Singleton { /** * @description:Private construction method to avoid external object creation */ private Singleton() {} /** * @description:Create an object of this class at the member location */ private static Singleton instance; /** * @description:Assignment in static code block */ static { instance = new Singleton(); } /** * @description:Provide a static method to get the object */ public static Singleton getInstance() { return instance; } }
package com.jie.creator.singleton.demo02; /** * @description:Test class * @author: jie * @time: 2022/1/29 16:04 */ public class Test { public static void main(String[] args) { //1. Create an object of the Singleton class Singleton singleton = Singleton.getInstance(); Singleton singleton1 = Singleton.getInstance(); //Judge whether the Singleton object obtained twice is the same object System.out.println(singleton==singleton1); } }
The result is true, indicating that singleton and singleton1 are the same memory address in the memory, so they are the same object.
In this way, a static variable of Singleton type is declared at the member position, and the object is created in the static code block and for the loading of the class. Therefore, it is basically the same as the starving Han style static variable method. Of course, this method also has the problem of memory waste.
2.3. Lazy (thread unsafe)
package com.jie.creator.singleton.demo03; /** * @description:Lazy: thread unsafe * @author: jie * @time: 2022/1/29 16:19 */ public class Singleton { /** * @description:Private construction method to avoid external object creation */ private Singleton() {} /** * @description:The object of this class created in the member position only declares a variable of this type and does not assign a value */ private static Singleton instance; /** * @description:Provide a static method to get the object */ public static Singleton getInstance() { //Determine whether instance is null If it is null, the Singleton class object has not been created //If not, create one and return. If yes, return directly if(instance == null) { instance = new Singleton(); } return instance; } }
package com.jie.creator.singleton.demo03; /** * @description:Test class * @author: jie * @time: 2022/1/29 16:04 */ public class Test { public static void main(String[] args) { //1. Create an object of the Singleton class Singleton singleton = Singleton.getInstance(); Singleton singleton1 = Singleton.getInstance(); //Judge whether the Singleton object obtained twice is the same object System.out.println(singleton==singleton1); } }
The result is true, indicating that singleton and singleton1 are the same memory address in the memory, so they are the same object.
From the above code, we can see that this method declares static variables of Singleton type at the member position without object assignment. The object of Singleton class is created only when getInstance() method is called to obtain the object of Singleton class, so as to realize the effect of lazy loading. However, if it is a multithreaded environment, thread safety problems will arise.
2.4. Lazy (thread safe)
package com.jie.creator.singleton.demo04; /** * @description:Lazy: thread safe * @author: jie * @time: 2022/1/29 16:49 */ public class Singleton { /** * @description:Private construction method to avoid external object creation */ private Singleton() {} /** * @description:The object of this class created in the member position only declares a variable of this type and does not assign a value */ private static Singleton instance; /** * @description:Provide a static method to get the object */ public static synchronized Singleton getInstance() { //Determine whether instance is null If it is null, the Singleton class object has not been created //If not, create one and return. If yes, return directly if(instance == null) { instance = new Singleton(); } return instance; } }
This method also realizes the effect of lazy loading and solves the problem of thread safety. However, the synchronized keyword is added to the getInstance() method, resulting in a particularly low execution effect of this method.
From the above code, we can see that the thread safety problem only occurs when initializing instance. Once the initialization is completed, it does not exist.
2.5. Lazy type (double check lock)
Let's discuss the problem of synchronized locking in lazy mode. For getInstance() method, most operations are read operations. Read operations are thread safe, so we don't have to make each thread hold a lock to call this method. We need to adjust the timing of locking. This also produces a new implementation mode: double check lock mode
package com.jie.creator.singleton.demo05; /** * @description:Lazy: double check * @author: jie * @time: 2022/1/29 16:56 */ public class Singleton { /** * @description:Private construction method to avoid external object creation */ private Singleton() {} /** * @description:The object of this class created in the member position only declares a variable of this type and does not assign a value */ private static Singleton instance; /** * @description:Provide a static method to get the object */ public static Singleton getInstance() { //In the first judgment, if the value of instance is not null, there is no need to preempt the lock and return the object directly if(instance == null) { synchronized (Singleton.class) { //Judge for the second time. After grabbing the lock, judge again whether it is null if(instance == null) { instance = new Singleton(); } } } return instance; } }
Double check lock mode is a very good singleton implementation mode, which solves the problems of singleton, performance and thread safety. The above double check lock mode looks perfect, but it is actually a problem. In the case of multithreading, null pointer problems may occur. The reason for the problem is that the JVM will optimize and reorder instructions when instantiating objects.
To solve the problem of null pointer exception caused by double check lock mode, you only need to use volatile keyword, which can ensure visibility and order.
The double check lock mode after adding volatile keyword can ensure thread safety and no performance problems in the case of multithreading.
2.6. Lazy type (static internal type)
In the static internal class singleton mode, the instance is created by the internal class. Because the JVM will not load the static internal class in the process of loading the external class, it will be loaded only when the properties / methods of the internal class are called, and its static properties will be initialized. Because static attributes are modified by static, they are guaranteed to be instantiated only once, and the instantiation order is strictly guaranteed.
package com.jie.creator.singleton.demo06; /** * @description:Lazy: static inner class * @author: jie * @time: 2022/1/29 17:47 */ public class Singleton { /** * @description:Private construction method to avoid external object creation */ private Singleton() {} /** * @description:Define a static inner class */ private static class SingletonHolder { //Declare and initialize the object of the external class in the internal class private static final Singleton INSTANCE = new Singleton(); } /** * @description:Provide a static method to get the object */ public static Singleton getInstance() { return SingletonHolder.INSTANCE; } }
The INSTANCE will not be initialized when the Singleton class is loaded for the first time. Only getInstance is called for the first time. The virtual machine loads the SingletonHolder and initializes the INSTANCE, which can not only ensure thread safety, but also ensure the uniqueness of the Singleton class.
Static inner class singleton mode is an excellent singleton mode, which is commonly used in open source projects. Without any lock, it ensures the safety of multithreading without any performance impact and waste of space.
2.7 enumeration method
Enumeration class implementation singleton mode is the highly recommended singleton implementation mode, because the enumeration type is thread safe and can only be loaded once. The designer makes full use of this feature of enumeration to realize the singleton mode. The writing method of enumeration is very simple, and the enumeration type is the only singleton implementation mode that will not be destroyed.
package com.jie.creator.singleton.demo07; /** * @description:Enumeration mode * @author: jie * @time: 2022/1/29 18:21 */ public enum Singleton { INSTANCE; }
package com.jie.creator.singleton.demo07; /** * @description:Test class * @author: jie * @time: 2022/1/29 18:24 */ public class Test { public static void main(String[] args) { //1. Create Singleton object Singleton singleton = Singleton.INSTANCE; Singleton singleton1 = Singleton.INSTANCE; //2. Determine whether the memory addresses are the same System.out.println(singleton==singleton1); } }
3. Existing problems
3.1 problem demonstration
Destroy singleton mode:
Enable the Singleton class defined above to create multiple objects, except the enumeration method (because the enumeration method is based on an implementation at the bottom of the JVM, which has solved all problems). There are two ways, serialization and reflection.
3.1.1 serialization and deserialization
package com.jie.creator.singleton.demo08; import java.io.Serializable; /** * @description:Lazy: static inner class * @author: jie * @time: 2022/1/29 17:47 */ public class Singleton implements Serializable { /** * @description:Private construction method to avoid external object creation */ private Singleton() {} /** * @description:Define a static inner class */ private static class SingletonHolder { //Declare and initialize the object of the external class in the internal class private static final Singleton INSTANCE = new Singleton(); } /** * @description:Provide a static method to get the object */ public static Singleton getInstance() { return SingletonHolder.INSTANCE; } }
package com.jie.creator.singleton.demo08; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; /** * @description:The test uses serialization and deserialization to break the singleton pattern * Desktop path C:\Users\jie\Desktop * @author: jie * @time: 2022/1/29 18:51 */ public class Test { public static void main(String[] args) throws Exception{ //Call write operation writeObject2File(); //Call read operation Singleton s1 = readObjectFromFile(); Singleton s2 = readObjectFromFile(); //Determine whether the addresses are the same System.out.println(s1==s2); } /** * @description:Read data from file (object) * @author: jie * @time: 2022/1/29 19:04 */ public static Singleton readObjectFromFile()throws Exception{ //1. Create object input stream object ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\jie\\Desktop\\a.txt")); //2. Read object Singleton singleton = (Singleton) ois.readObject(); //Release resources ois.close(); return singleton; } /** * @description:Write data (objects) to a file * @author: jie * @time: 2022/1/29 18:55 */ public static void writeObject2File() throws Exception{ //1. Get Singleton object Singleton instance = Singleton.getInstance(); //2. Create object (output stream object) ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\jie\\Desktop\\a.txt")); //3. Write object oos.writeObject(instance); //4. Release resources oos.close(); } }
Test results:
The running result of the above code is false, indicating that serialization and deserialization have broken the singleton design pattern.
3.1.2 reflection
package com.jie.creator.singleton.demo09; import java.io.Serializable; /** * @description:Lazy: static inner class * @author: jie * @time: 2022/1/29 17:47 */ public class Singleton implements Serializable { /** * @description:Private construction method to avoid external object creation */ private Singleton() {} /** * @description:Define a static inner class */ private static class SingletonHolder { //Declare and initialize the object of the external class in the internal class private static final Singleton INSTANCE = new Singleton(); } /** * @description:Provide a static method to get the object */ public static Singleton getInstance() { return SingletonHolder.INSTANCE; } }
package com.jie.creator.singleton.demo09; import java.lang.reflect.Constructor; /** * @description:The test uses reflection destruction singleton mode * @author: jie * @time: 2022/1/29 19:26 */ public class Test { public static void main(String[] args) throws Exception{ //1. Gets the bytecode object of Singleton Class clazz = Singleton.class; //2. Get parameterless constructor object Constructor cons = clazz.getDeclaredConstructor(); //3. Cancel access check cons.setAccessible(true); //4. Create Singleton object Singleton s1 = (Singleton) cons.newInstance(); Singleton s2 = (Singleton) cons.newInstance(); //Judge that if the return is true, it indicates that the singleton mode is not broken. If false, it indicates that the singleton mode is broken System.out.println(s1==s2); } }
Test results:
The running result of the above code is false, indicating that reflection has broken the singleton design pattern
3.2 problem solving
3.2.1 solution to destroy the singleton mode by serialization and deserialization
Add the readResolve() method to the Singleton class. It is called by reflection during deserialization. If this method is defined, it returns the value of this method. If it is not defined, it returns the new object.
Take a look at the test results of the test class:
The above readResolve() method annotation says that this method will be called automatically when deserialization is performed. Will it be called automatically? Now we're going to study a principle of its bottom layer and see if it's like this.
Look at the underlying code of which method? It's actually the readObject() method.
Because it is used to read objects, is it new when it reads? Or call the readResolve() method in Singleton? Come and have a look right away.
Source code analysis:
ObjectInputStream class
Hold down CTRL and click in.
Hold down CTRL and click in.
ok, here is a simple look at the source code.
3.2.2 solution of single case cracking in reflection mode
Test results:
This way is easier to understand. When the constructor is called for creation through reflection, an exception is thrown directly. Do not run this operation.
4. JDK source code parsing - Runtime class
In our JDK source code, which classes use the singleton design pattern? Here we will introduce a class - Runtime
The Runtime class is the singleton design pattern used.
Check which singleton mode is used through the source code?
From the above source code, we can see that the Runtime class uses starving Han style (static variable) to implement the singleton mode.
Then we can write a test class and use the methods in the Runtime class. You can copy and run it.
package com.jie.creator.singleton.demo10; import java.io.InputStream; /** * @description:Using methods in the Runtime class * @author: jie * @time: 2022/1/29 21:48 */ public class Test { public static void main(String[] args) throws Exception{ //Get Runtime class object Runtime runtime = Runtime.getRuntime(); //Call the method exec of the Runtime object. The parameter is a command Process exec = runtime.exec("ipconfig"); //Call the method of the process object to get the input stream InputStream is = exec.getInputStream(); //Define a byte array byte[] arr = new byte[1024*1024*100]; //Read data returns the number of bytes read int len = is.read(arr); //Converts the byte array into a string and outputs it to the console System.out.println(new String(arr,0,len,"GBK")); } }
5. Summary
-
In the single example, two hungry Chinese expressions (static variables and static code) are available, but there are performance problems
-
In the single example, two lazy types (thread unsafe and thread safe) are not recommended. There is a thread safety problem. The thread safe method solves the thread problem, but the performance is very poor
-
The last three singleton modes (double check lock, static inner class, enumeration mode) are recommended
-
matters needing attention
-
There is only one object of this class in the system memory, which saves system resources. For some objects that need to be created and destroyed frequently, using singleton mode can improve the system performance
-
When you want to instantiate a singleton class, you must remember to use the corresponding method to get the object instead of new
-
-
application
-
Runtime class in jdk source code
-
ApplicationContext class in tomcat
-
session factory
-
6. Code warehouse address
Creator mode case code: the case code used to install the creator mode