Writing in the front

  • Take notes on learning design patterns
  • Improve the flexible use of design patterns

Learning to address

www.bilibili.com/video/BV1G4…

www.bilibili.com/video/BV1Np…

Refer to the article

C.biancheng.net/view/1317.h…

Program source codeGitee.com/zhuang-kang…

4. Creator mode

The main focus of the creative pattern is “How do you create objects?” , its main feature is “the separation of object creation and use”.

This reduces the coupling of the system and eliminates the need for users to focus on object creation details.

The creation mode is divided into:

  • The singleton pattern
  • Factory method pattern
  • Abstract engineering model
  • The prototype pattern
  • Builder model

5. Singleton pattern

5.1 Definition and features of singleton Mode

Definition of a Singleton pattern: a pattern in which a class has only one instance and the class can create that instance itself. For example, only one task manager can be opened in Windows, so as to avoid the waste of memory resources caused by opening multiple task manager Windows, or the inconsistent display content of each window and other errors.

In a computer system, And Windows in the recycle bin, operating systems, file systems, the threads in a multi-threaded pool, graphics driver object, printer background processing services, the application log object, database connection pool, counter website, Web application configuration object, dialog box, and the system cache in the application program is often designed to be a singleton.

Singleton model is also widely used in real life, such as company CEO, department manager, etc. ServletgContext and ServletContextConfig in J2EE standard, ApplicationContext in Spring framework application, connection pool in database are also singletons.

The singleton pattern has three characteristics:

  1. A singleton class has only one instance object;
  2. The singleton object must be created by the singleton class itself;
  3. A singleton class externally provides a global access point to that singleton.

Advantages of the singleton pattern:

  • The singleton ensures that there is only one instance in memory, reducing the memory overhead.

  • Multiple occupancy of resources can be avoided.

  • The singleton mode sets global access points that optimize and share access to resources.

    Disadvantages of the singleton pattern:

    • The singleton mode generally has no interface and is difficult to expand. If you want to extend, there is no other way than to modify the original code, violating the open closed principle.
    • In concurrent testing, the singleton pattern is not conducive to code debugging. During debugging, you cannot simulate generating a new object if the code in the singleton is not executed.
    • The singleton function code is usually written in a class, and it is easy to violate the single responsibility principle if the function is not properly designed.

5.2 Structure and implementation of singleton pattern

5.2.1 Singleton pattern structure

  1. Singleton class: a class that contains an instance and can create its own instance.
  2. Access classes: Classes that use singletons.

5.2 Code Implementation

There are two types of singleton design patterns:

Hungry: Class loading causes the singleton to be created

Lazy: Class loading does not cause the singleton to be created, but only when the object is first used

Hungry (static variable)

package com.zhuang.singleton.type1;

/ * * *@Classname SingletonTest01
 * @DescriptionHanky-hank (static variable) *@Date 2021/3/17 9:26
 * @Created by dell
 */

public class SingletonTest01 {
    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        // Check whether it is a singleton
        System.out.println(instance == instance2);
        System.out.println("Hash of intstance" + instance.hashCode());
        System.out.println("Hash of intstance2"+ instance2.hashCode()); }}class Singleton {

    //1. Constructor privatized, external can new,
    private Singleton(a) {}// Create object instances within this class
    private final static Singleton instance = new Singleton();

    // Provide a public static method externally
    public static Singleton getInstance(a) {
        returninstance; }}Copy the code

Description:

This method declares static variables of the Singleton type at member locations and creates an object instance of the Singleton class. The Instance object is created as the class is loaded. If the object is large enough to remain unused, memory is wasted.

Static code block

package com.zhuang.singleton.type2;

/ * * *@Classname SingletonTest02
 * @DescriptionStatic code block *@Date 2021/3/17 9:35
 * @Created by dell
 */

public class SingletonTest02 {
    public static void main(String[] args) {
        Singleton2 instance = Singleton2.getInstance();
        Singleton2 instance2 = Singleton2.getInstance();
        // Check whether it is a singleton
        System.out.println(instance == instance2);
        System.out.println("Hash of intstance" + instance.hashCode());
        System.out.println("Hash of intstance2"+ instance2.hashCode()); }}class Singleton2 {

    //1. Constructor privatized, external can new,
    private Singleton2(a) {}// Create object instances within this class
    private static Singleton2 instance;

    /* Create an object in a static code block */
    static {
        instance = new Singleton2();
    }

    // Provide a public static method externally
    public static Singleton2 getInstance(a) {
        returninstance; }}Copy the code

Description:

Static variables of the Singleton type are declared at member locations, and object creation is done in static code blocks, which are also created for class loading. So it’s basically the same as Hungry Hungry method 1, but of course there’s a memory waste problem.

Lazy threads are not safe

package com.zhuang.singleton.type3;

/ * * *@Classname SingletonTest03
 * @DescriptionLazy threads are not safe@Date2021/3/17 and *@Created by dell
 */

public class SingletonTest03 {
    public static void main(String[] args) {
        System.out.println("Lazy threads are not safe!!");
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        // Check whether it is a singleton
        System.out.println(instance == instance2);
        System.out.println("Hash of intstance" + instance.hashCode());
        System.out.println("Hash of intstance2"+ instance2.hashCode()); }}class Singleton {
    private static Singleton instance;

    private Singleton(a) {}// Provide a static public method to create instance only when the method is used
    public static Singleton getInstance(a) {
        if (instance == null) {
            instance = new Singleton();
        }
        returninstance; }}Copy the code

Description:

Static variables of type Singleton are declared at member positions without assigning to the object. When was the assignment done? The Singleton class object is created when the getInstance() method is called to get the Singleton class object, thus implementing the lazy loading effect. However, if you are in a multi-threaded environment, there are thread safety issues.

Lazy (thread-safe, synchronous approach)

package com.zhuang.singleton.type4;

/ * * *@Classname SingletonTest04
 * @DescriptionLazy (thread-safe, synchronous approach) *@Date2021/3/17.so *@Created by dell
 */

public class SingletonTest04 {
    public static void main(String[] args) {
        System.out.println("Slacker, thread safe!!");
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        // Check whether it is a singleton
        System.out.println(instance == instance2);
        System.out.println("Hash of intstance" + instance.hashCode());
        System.out.println("Hash of intstance2"+ instance2.hashCode()); }}class Singleton {
    private static Singleton instance;

    private Singleton(a) {}// Provide a static public method with synchronized code to address thread-safety issues
    public static synchronized Singleton getInstance(a) {
        if (instance == null) {
            instance = new Singleton();
        }
        returninstance; }}Copy the code

Description:

This method also achieves lazy loading effect and solves the problem of thread safety. But the addition of the synchronized keyword to the getInstance() method results in particularly poor performance. As you can see from the above code, thread-safety issues occur only when instance is initialized, and disappear once the initialization is complete.

Lazy (thread-safe, synchronized code blocks)

package com.zhuang.singleton.type5;

/ * * *@Classname SingletonTest05
 * @DescriptionLazy (thread-safe, synchronized code blocks) *@Date 2021/3/17 9:50
 * @Created by dell
 */

public class SingletonTest05 {
    public static void main(String[] args) {
        System.out.println("Lazy, thread-safe! Sync code block");
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        // Check whether it is a singleton
        System.out.println(instance == instance2);
        System.out.println("Hash of intstance" + instance.hashCode());
        System.out.println("Hash of intstance2"+ instance2.hashCode()); }}class Singleton {
    private static Singleton instance;

    private Singleton(a) {}// Provide a static public method with synchronized code to address thread-safety issues
    public static synchronized Singleton getInstance(a) {
        if (instance == null) {
            synchronized (Singleton.class) {
                instance = newSingleton(); }}returninstance; }}Copy the code

Double-checked locking mode is a kind of very good singleton implementation pattern, solve the singleton, performance, thread safety problem, the double checking the lock mode looks perfect, but there are problems, in the case of multi-threaded, there might be null pointer, the cause of the problem is the JVM at the time of instantiation objects will reorder operation optimization and instructions.

To solve the problem of null-pointer exceptions caused by double-checked locking, use only the volatile keyword, which guarantees visibility and order.

package com.zhuang.singleton.type6;

/ * * *@Classname SingletonTest06
 * @DescriptionDouble check, * is recommended@Date2021/3/17 hastily *@Created by dell
 */

public class SingletonTest06 {
    public static void main(String[] args) {
        System.out.println("Slob, double check, recommended.");
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        // Check whether it is a singleton
        System.out.println(instance == instance2);
        System.out.println("Hash of intstance" + instance.hashCode());
        System.out.println("Hash of intstance2"+ instance2.hashCode()); }}class Singleton {
    private static volatile Singleton instance;

    private Singleton(a) {}// Provide a static public method, add double-checking code, add synchronous processing code, solve lazy loading problem
    // Ensure efficiency. It is recommended to use
    public static synchronized Singleton getInstance(a) {
        if (instance == null) {
            synchronized (Singleton.class) {
                instance = newSingleton(); }}returninstance; }}Copy the code

Summary:

The double-checked locking pattern with the volatile keyword is a good singleton implementation that ensures thread-safety without performance issues in multithreaded situations.

Static inner classes implement the singleton pattern!

package com.zhuang.singleton.type7;

/ * * *@Classname SingletonTest07
 * @DescriptionStatic inner classes implement the singleton pattern! *@Date2021/3/17 he said *@Created by dell
 */

public class SingletonTest07 {
    public static void main(String[] args) {
        System.out.println("Static inner class implementation singleton pattern");
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        // Check whether it is a singleton
        System.out.println(instance == instance2);
        System.out.println("Hash of intstance" + instance.hashCode());
        System.out.println("Hash of intstance2"+ instance2.hashCode()); }}class Singleton {
    private static Singleton instance;

    private Singleton(a) {}// Write a static inner class with a static attribute, Singleton
    public static class SingletonInstance {
        private static final Singleton INSTANCE = new Singleton();
    }

    / / to provide a public static method, direct return SingletonInstance. INSTANCE;
    public static synchronized Singleton getInstance(a) {
        returnSingletonInstance.INSTANCE; }}Copy the code

Description:

INSTANCE is not initialized the first time the Singleton class is loaded. Only getInstance is called the first time the virtual machine loads the SingletonHolder

And initialize INSTANCE, which ensures not only thread-safety but also the uniqueness of the Singleton class.

Summary:

The static inner class singleton pattern is an excellent singleton pattern, which is commonly used in open source projects. In the case of no lock, to ensure the safety of multi-threading, and no performance impact and space waste.

Enumeration of the way to achieve the singleton pattern

package com.zhuang.singleton.type8;

/ * * *@Classname SingletonTest08
 * @DescriptionEnumeration to implement the singleton pattern *@Date 2021/3/17 10:06
 * @Created by dell
 */

public class SingletonTest08 {
    public static void main(String[] args) {
        System.out.println("Enumeration is recommended to implement singleton pattern.");
        Singleton instance = Singleton.INSTANCE;
        Singleton instance2 = Singleton.INSTANCE;
        System.out.println(instance == instance2);
        // Check whether it is a singleton
        System.out.println(instance == instance2);
        System.out.println("Hash of intstance" + instance.hashCode());
        System.out.println("Hash of intstance2"+ instance2.hashCode()); }}/ * enumeration * /
enum Singleton {
    INSTANCE;/ / property

    public void method(a) {
        System.out.println("Method () method is called..."); }}Copy the code

Description:

The enumeration mode belongs to the evil Chinese mode.

5.3 Application Scenarios of singleton Mode

  • For classes that need to be created frequently, using singletons can reduce memory stress on the system and reduce GC.
  • When a class requires only one object to be generated, such as the monitor of a class, the ID number of each person, etc.
  • Some classes take a lot of resources to create instances, or take a long time to instantiate, and are often used.
  • When a class needs frequent instantiation and the objects created are frequently destroyed, such as a multithreaded thread pool, network connection pool, etc.
  • Objects that frequently access databases or files.
  • For some control hardware level operations, or operations that should be a single control logic from a system perspective, if there are multiple instances, the system will be completely messed up.
  • When objects need to be shared. Because the singleton pattern allows you to create only one object, sharing that object saves memory and speeds up object access. For example, configuration objects on the Web and connection pools of databases.

5.4 Existing problems

Failure singleton mode:

Enable the Singleton class defined above to create multiple objects, excluding enumerations. There are two ways, serialization and reflection.

  • Serialization deserialization

    The Singleton class:

    public class Singleton implements Serializable {
    
        // Private constructor
        private Singleton(a) {}
    
        private static class SingletonHolder {
            private static final Singleton INSTANCE = new Singleton();
        }
    
        // Provide static methods to get the object
        public static Singleton getInstance(a) {
            returnSingletonHolder.INSTANCE; }}Copy the code

    The Test class:

    public class Test {
        public static void main(String[] args) throws Exception {
            // Write objects to a file
            //writeObject2File();
            // Read objects from files
            Singleton s1 = readObjectFromFile();
            Singleton s2 = readObjectFromFile();
    
            // Determine if two deserialized objects are the same object
            System.out.println(s1 == s2);
        }
    
        private static Singleton readObjectFromFile(a) throws Exception {
            // Create an object input stream object
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\dell\\Desktop\\a.txt"));
            // Read the Singleton object first
            Singleton instance = (Singleton) ois.readObject();
    
            return instance;
        }
    
        public static void writeObject2File(a) throws Exception {
            // Get the Singleton class object
            Singleton instance = Singleton.getInstance();
            // Create an object output stream
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\dell\\Desktop\\a.txt"));
            // Write the instance object into the fileoos.writeObject(instance); }}Copy the code

    The above code results in false, indicating that serialization and deserialization have broken the singleton design pattern.

  • reflection

    The Singleton class:

    public class Singleton {
    
        // Private constructor
        private Singleton(a) {}
        
        private static volatile Singleton instance;
    
        // Provide static methods to get the object
        public static Singleton getInstance(a) {
    
            if(instance ! =null) {
                return instance;
            }
    
            synchronized (Singleton.class) {
                if(instance ! =null) {
                    return instance;
                }
                instance = new Singleton();
                returninstance; }}}Copy the code

    The Test class:

    public class Test {
        public static void main(String[] args) throws Exception {
            // Get the bytecode object of the Singleton class
            Class clazz = Singleton.class;
            // Get the private, no-argument constructor object of the Singleton class
            Constructor constructor = clazz.getDeclaredConstructor();
            // Cancel the access check
            constructor.setAccessible(true);
    
            // Create object s1 for the Singleton class
            Singleton s1 = (Singleton) constructor.newInstance();
            // Create object s2 of the Singleton class
            Singleton s2 = (Singleton) constructor.newInstance();
    
            // Determine if the two Singleton objects created by reflection are the same objectSystem.out.println(s1 == s2); }}Copy the code

    The above code results in false, indicating that serialization and deserialization have broken the singleton design pattern

Note: Enumerations do not have either of these problems.

Problem solving

  • Serialized, unsequentialized solutions to break singleton patterns

    Add the readResolve() method to the Singleton class, which is called by reflection during deserialization and returns the value of the method if defined, or the newly generated object if not.

    The Singleton class:

    public class Singleton implements Serializable {
    
        // Private constructor
        private Singleton(a) {}
    
        private static class SingletonHolder {
            private static final Singleton INSTANCE = new Singleton();
        }
    
        // Provide static methods to get the object
        public static Singleton getInstance(a) {
            return SingletonHolder.INSTANCE;
        }
        
        /** * the following is to solve serialization deserialization crack singleton pattern */
        private Object readResolve(a) {
            returnSingletonHolder.INSTANCE; }}Copy the code

    Source code analysis:

    ObjectInputStream class

    public final Object readObject(a) throws IOException, ClassNotFoundException{...// if nested read, passHandle contains handle of enclosing object
        int outerHandle = passHandle;
        try {
            Object obj = readObject0(false);// focus on the readObject0 method. }private Object readObject0(boolean unshared) throws IOException {...try {
    		switch (tc) {
    			...
    			case TC_OBJECT:
    				return checkResolve(readOrdinaryObject(unshared));// Focus on the readOrdinaryObject method. }}finally{ depth--; bin.setBlockDataMode(oldMode); }}private Object readOrdinaryObject(boolean unshared) throws IOException {...//isInstantiable returns true, executes desc.newinstance () to create a new singleton class by reflection,
        obj = desc.isInstantiable() ? desc.newInstance() : null; ./ / desc. After readResolve method is added in the Singleton class hasReadResolveMethod () method performs the result is true
        if(obj ! =null && handles.lookupException(passHandle) == null && desc.hasReadResolveMethod()) {
        	// Call the readResolve method in the Singleton class via reflection to assign the return value to the rep variable
        	// Calling ObjectInputStream's readObject method multiple times will then call our readResolve method, so the same object is returned.Object rep = desc.invokeReadResolve(obj); . }return obj;
    }
    Copy the code
  • Reflection mode crack singleton solution

    public class Singleton {
    
        // Private constructor
        private Singleton(a) {
            /* Reflection break singleton pattern need to add code */
            if(instance ! =null) {
                throw newRuntimeException(); }}private static volatile Singleton instance;
    
        // Provide static methods to get the object
        public static Singleton getInstance(a) {
    
            if(instance ! =null) {
                return instance;
            }
    
            synchronized (Singleton.class) {
                if(instance ! =null) {
                    return instance;
                }
                instance = new Singleton();
                returninstance; }}}Copy the code

    Description:

    It’s easier to understand this way. An exception is thrown directly when the constructor is called to create a create by reflection. Do not run this operation.

5.5 JDK source code parsing -Runtime classes

The Runtime class is the singleton design pattern used.

  1. Which singleton pattern is used to view from the source code

    public class Runtime {
        private static Runtime currentRuntime = new Runtime();
    
        /**
         * Returns the runtime object associated with the current Java application.
         * Most of the methods of class <code>Runtime</code> are instance
         * methods and must be invoked with respect to the current runtime object.
         *
         * @return  the <code>Runtime</code> object associated with the current
         *          Java application.
         */
        public static Runtime getRuntime(a) {
            return currentRuntime;
        }
    
        /** Don't let anyone else instantiate this class */
        private Runtime(a) {}... }Copy the code

    As you can see from the source code above, the Runtime class implements the singleton pattern using the hangul (static attribute) approach.

  2. Use the methods in the Runtime class

    public class RuntimeDemo {
        public static void main(String[] args) throws IOException {
            // Get the Runtime object
            Runtime runtime = Runtime.getRuntime();
    
            // Returns the total amount of memory in the Java virtual machine.
            System.out.println(runtime.totalMemory());
            // Returns the maximum amount of memory that the Java virtual machine is trying to use.
            System.out.println(runtime.maxMemory());
    
            // Create a new process to execute the specified string command and return the process object
            Process process = runtime.exec("ipconfig");
            // Obtain the result of executing the command from the input stream
            InputStream inputStream = process.getInputStream();
            byte[] arr = new byte[1024 * 1024* 100];
            int b = inputStream.read(arr);
            System.out.println(new String(arr,0,b,"gbk")); }}Copy the code

Write in the last

  • If my article is useful to you, please give me a click 👍, thank you 😊!
  • If you have any questions, please point them out in the comments section! 💪