1. Deeply study the curriculum arrangement of the underlying implementation principle of single case
Course content
- Basic design ideas and concepts of singleton pattern
- Application scenario of singleton mode
- Multiple ways to write single case mode
- Why do lazy people need double check locks
- How do you create objects in Java
- How to prevent a single instance from being reflected, serialized and cracked
- The underlying implementation principle of creating objects by deserialization
2. Basic concepts, advantages and disadvantages of singleton mode
Basic concept of single case
There will only be one instance in the Jvm
Advantages and disadvantages of single case
advantage:
- Objects will not be created frequently to save server memory;
- There is only one instance to realize reuse, there is no need to create objects, and the access speed is relatively fast;
Disadvantages:
- Because the object has only one instance, when multiple threads share the same singleton object at the same time, there may be thread safety problems;
- Singleton objects are generally created and stored in the persistent area with static. They will not be recycled by the garbage collection mechanism and will always occupy the server memory. If they are created too much, the server memory may overflow;
Single case application scenario
- The configuration files defined in the project are all single instances;
- Spring IOC container creates object default singleton (reuse);
- Thread pool and database connection pool;
- The Servlet object defaults to a singleton, which is thread unsafe, and spring MVC encapsulation based on Servlet is also unsafe;
- The Jvm has a built-in caching framework. The bottom layer is based on HashMap encapsulation + elimination strategy, and the HashMap object is a single instance;
- Enumerations, constants
- Site counter
3 handwriting singleton mode
Lazy concept: the object is created only when it is really needed.
Note: you need to privatize the constructor
Handwritten lazy (thread unsafe)
public class Singleton01 { private static Singleton01 singleton01 = null; /** * Privatization */ private Singleton01() { System.out.println("singleton01"); } public static Singleton01 getInstance() { if (singleton01 == null) { singleton01 = new Singleton01(); } return singleton01; } public static void main(String[] args) { Singleton01 instance1 = Singleton01.getInstance(); Singleton01 instance2 = Singleton01.getInstance(); System.out.println(instance1 == instance2); } }
Operation results:
This writing method: if multiple threads access at the same time, it may cause thread insecurity, create multiple objects, and violate the single instance uniqueness principle.
Thread safety problem: multiple threads share the same global variable at the same time. Thread safety problems may occur when writing.
Handwritten lazy (thread safe)
public class Singleton02 { private static Singleton02 singleton02; private Singleton02(){ } // When synchronized is added to the method, it will also be locked when reading, which is inefficient public static synchronized Singleton02 getInstance() { if (singleton02 == null) { singleton02 = new Singleton02(); } return singleton02; } public static void main(String[] args) { Singleton02 instance1 = Singleton02.getInstance(); Singleton02 instance2 = Singleton02.getInstance(); System.out.println(instance1 == instance2); // true } }
This writing method: although it can ensure thread safety, as long as the object is created, the lock needs to be obtained when obtaining the object later, which is very inefficient.
Lazy double check lock
public class Singleton03 { private static Singleton03 singleton03; private Singleton03() { } public static Singleton03 getInstance() { // The first judgment of locking is to ensure that the object will be locked only when it is created, and the acquired object will not be locked if (singleton03 == null) { synchronized (Singleton03.class) { // The second judgment ensures that the following code processes a logical single thread to prevent the creation of multiple objects if (singleton03 == null) { singleton03 = new Singleton03(); } } } return singleton03; } public static void main(String[] args) { Singleton03 instance1 = Singleton03.getInstance(); Singleton03 instance2 = Singleton03.getInstance(); System.out.println(instance1 == instance2); } }
This writing method: it can not only solve the thread safety problem (two judgments ensure that the created object is unique), but also has high efficiency (the lock will not be entered when obtaining the object)
The difference between lazy and hungry
Lazy: the object will be created only when it is really needed, which relatively saves memory space. Disadvantages: the thread is inherently unsafe, so it is necessary to ensure thread safety through code in the later stage;
Hungry Chinese style: the object will be created when the class file is loaded. It is inherently thread safe (only loaded once). The disadvantage is that it wastes memory space.
Hungry Han implementation singleton (thread safety)
public class Singleton04 { // This object is created when the class file is loaded // private static Singleton04 singleton04 = new Singleton04(); // Create constant writing public static final Singleton04 singleton04 = new Singleton04(); private Singleton04() { System.out.println("singleton04"); } public static Singleton04 getInstance() { return singleton04; } public static void main(String[] args) { // Singleton04 instance1 = Singleton04.getInstance(); // Singleton04 instance2 = Singleton04.getInstance(); // System.out.println(instance1 == instance2); System.out.println(Singleton04.getInstance() == Singleton04.getInstance()); } }
Constants defined in the project are in the form of hungry Chinese. If too many constants are defined, it may lead to memory overflow.
Implementation of singleton based on static code block
public class Singleton05 { private static Singleton05 singleton05 = null; private Singleton05() { } static { System.out.println("current class Loaded"); singleton05 = new Singleton05(); } public static Singleton05 getInstance() { return singleton05; } public static void main(String[] args) { // Singleton05 instance1 = Singleton05.getInstance(); // Singleton05 instance2 = Singleton05.getInstance(); // System.out.println(instance1 == instance2); } }
In hungry Chinese style, class is initialized when it is loaded.
4 how to use reflection mechanism to crack single cases & how to defend
How can objects be created in the Jvm?
Reflection mechanism, serialization, new, cloning technology
public class Test001 { public static void main(String[] args) throws Exception { Singleton01 instance1 = Singleton01.getInstance(); // Create objects using the reflection mechanism Class<?> aClass = Class.forName("com.mayikt.Singleton01"); // Constructor<?> constructor = aClass.getConstructor(); // Get all constructors, including those of the parent class Object, and report an error Constructor<?> constructor = aClass.getDeclaredConstructor();// Gets the parameterless constructor of the current class, excluding the parent class constructor.setAccessible(true); Singleton01 instance2 = (Singleton01) constructor.newInstance(); System.out.println(instance1 == instance2); } }
Operation results:
How to defend against single instance of reflection cracking
public class Singleton01 { private static Singleton01 singleton01 = null; /** * Privatization */ private Singleton01() throws Exception { if (singleton01 != null) { throw new Exception("This object has already been created and cannot be created repeatedly"); } else { singleton01 = this; } System.out.println("singleton01"); } public static Singleton01 getInstance() throws Exception { if (singleton01 == null) { singleton01 = new Singleton01(); } return singleton01; } public static void main(String[] args) throws Exception { Singleton01 instance1 = Singleton01.getInstance(); Singleton01 instance2 = Singleton01.getInstance(); System.out.println(instance1 == instance2); } }
Operation results:
If there is no else part, when the reflection creation object is called before getInstance() creates the object, the defense is invalid.
5 use serialization to destroy singleton & how to defend
Serialization: convert the object into binary form and store it directly on the hard disk for persistence;
Deserialization: read binary files from local files and convert them into objects;
public class Test003 { public static void main(String[] args) throws Exception { // 1. The object needs to be serialized to local storage FileOutputStream fos = new FileOutputStream("d:/code/singleton.txt"); ObjectOutputStream oos = new ObjectOutputStream(fos); Singleton01 instance1 = new Singleton01(); oos.writeObject(instance1); oos.close(); fos.close(); //2. Deserialize objects from hard disk to memory ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/code/singleton.txt")); Singleton01 instance2 = (Singleton01) ois.readObject(); System.out.println(instance1 == instance2); } }
Operation results:
Serialization creates objects that violate the singleton design principle
doubt:
- Why does serialization create a new object without a parameterless constructor?
- How to create a new object
How to create a new object
public class Singleton01 implements Serializable { private static Singleton01 singleton01 = null; /** * Privatization */ Singleton01() throws Exception { if (singleton01 != null) { throw new Exception("This object has already been created and cannot be created repeatedly"); } else { singleton01 = this; } System.out.println("singleton01"); } public static Singleton01 getInstance() throws Exception { if (singleton01 == null) { singleton01 = new Singleton01(); } return singleton01; } public static void main(String[] args) throws Exception { Singleton01 instance1 = Singleton01.getInstance(); Singleton01 instance2 = Singleton01.getInstance(); System.out.println(instance1 == instance2); } /** * The serial number generation callback method is used to deserialize and generate a singleton object * The method name must be fixed write dead readResolve * @return */ public Object readResolve() { return singleton01; } }
Operation results:
6 serialization how to generate a new object
How does serialization generate a new object
Creating an Object in the form of serialization does not go through the serialization class parameterless constructor. Judge whether the serialization class implements the Serializable interface. If the interface is implemented, transfer the parameterless constructor of the class's parent class (which does not implement the Serializable interface) to instantiate the Object through the parent class. If the parent class also implements the Serializable interface, initialize the Object through the Object class.
public class Singleton01 extends UserEntity implements Serializable { private static Singleton01 singleton01 = null; /** * Privatization */ Singleton01() throws Exception { if (singleton01 != null) { throw new Exception("This object has already been created and cannot be created repeatedly"); } else { singleton01 = this; } System.out.println("singleton01"); } public static Singleton01 getInstance() throws Exception { if (singleton01 == null) { singleton01 = new Singleton01(); } return singleton01; } public static void main(String[] args) throws Exception { Singleton01 instance1 = Singleton01.getInstance(); Singleton01 instance2 = Singleton01.getInstance(); System.out.println(instance1 == instance2); } /** * The serial number generation callback method is used to deserialize and generate a singleton object * The method name must be fixed write dead readResolve * @return */ public Object readResolve() { return singleton01; } }
public class UserEntity extends Object implements Serializable { public UserEntity() { System.out.println("Parent class initialization started"); } }
Operation results:
How the readResolve method executes
When serialization creates an object, judge whether there is a readResolve method in the serialization class. If so, directly call the readResolve method in the serialization class to return a singleton object.
7 enumeration is the safest singleton
Serialization and reflection cracking can not be solved at the bottom
Enumerate the safest singletons:
- Congenital default guarantee singleton for objects generated by serialization
- Reflection failed to initialize to enumeration
public enum Singleton06 { INSTANCE; public void addUser(){ System.out.println("Enumeration class, congenital security"); } Singleton06(){ System.out.println(">>Parameterless constructor execution"); } }
public class Test004 { public static void main(String[] args) throws Exception { // Singleton06 instance1 = Singleton06.INSTANCE; // Singleton06 instance2 = Singleton06.INSTANCE; // System.out.println(instance1 == instance2); // // Class<?> aClass = Class.forName("com.mayikt.Singleton06"); // aClass.newInstance(); // report errors // 1. The object needs to be serialized to local storage FileOutputStream fos = new FileOutputStream("d:/code/enum.txt"); ObjectOutputStream oos = new ObjectOutputStream(fos); Singleton06 instance1 = Singleton06.INSTANCE; oos.writeObject(instance1); oos.close(); fos.close(); //2. Deserialize objects from hard disk to memory ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/code/enum.txt")); Singleton06 instance2 = (Singleton06) ois.readObject(); System.out.println(instance1 == instance2); } }
Operation results:
Source code download address (mayikt_designPattern_6.zip):
Link: https://pan.baidu.com/s/1wWKZN1MbXICZVW1Vxtwe6A
Extraction code: fire