Java Design Patterns (Chapter 5: Singleton Pattern)

Chapter 5: Singleton Design Pattern

5.1 Introduction to singleton design pattern

Singleton design pattern: It is to adopt a certain method to ensure that in the entire software system, only one object instance can exist for a certain class, and this class only provides a method (static method) for the system to obtain its object instance.

5.2 Key points of the singleton design pattern:

5.2.1 Point 1: There can only be one instance of a class

A certain class can only have one instance ------> Constructor privatization

5.2.2 The second point: it must create this instance by itself

It must create this instance by itself ------> Contains a static variable of this class to hold this unique instance

5.2.3 The third point: it must provide this instance to the whole system by itself

It must provide this instance to the whole system by itself ------> two methods

Provide external ways to obtain the instance object: ① direct exposure (variable permission is public), ② use static variable get method to obtain (variable permission is private, method permission is public).

5.2.4 Fourth point: Emphasize that this is a singleton

Emphasize that this is a singleton ------> use final to modify

5.3 Eight ways of singleton design pattern

  1. Hungry style (static constant)
  2. Hungry Chinese style (static code block)
  3. Lazy (not thread safe)
  4. Lazy (thread-safe, synchronized methods)
  5. Lazy (thread-safe, synchronized code blocks)
  6. double check
  7. static inner class
  8. enumerate

5.4 Hungry style (static constant):

Steps: ①Constructor privatization ------> ②Create object inside the class ------> ③Expose a static public method getInstance() to the outside

public class Singleton1 {
    
    
    private static final Singleton1 INSTANCE_1 = new Singleton1();

    private Singleton1() {
    
    
    }

    public static Singleton1 getInstance() {
    
    
        return INSTANCE_1;
    }
}
//测试,发现instance和instance2的哈希值是一致的,这两个变量都是同一个对象。
public void test1(){
    
    
    Singleton1 instance = Singleton1.getInstance();
    Singleton1 instance2 = Singleton1.getInstance();
    System.out.println(instance == instance2);//true
}

Explanation of advantages and disadvantages:

  • Advantages: The writing method is simple, and the instantiation is completed when the class is loaded, avoiding the problem of thread synchronization.

  • Disadvantage: The instantiation is completed when the class is loaded, and the effect of Lazy Loading is not achieved. If this instance is never used from the beginning to the end, it will cause a waste of memory.

  • This method avoids multi-thread synchronization problems based on the ClassLoader class loading mechanism, but the instance is instantiated when the class is loaded, and most of the singleton mode calls the getInstance method, but there are many reasons for class loading, so It is not sure that there are other ways (other static methods) to cause class loading. At this time, initializing the instance does not achieve the effect of Lazy Loading.

Summary : This singleton mode is available, but it may cause memory waste.

5.5 Hungry style (static code block):

public class Singleton2 {
    
    
    private static final Singleton2 INSTANCE_2;
    private String info;

    static {
    
    
        try {
    
    
            Properties properties = new Properties();
            ClassLoader classLoader = Singleton2.class.getClassLoader();
            InputStream is = classLoader.getResourceAsStream("single.properties");
            properties.load(is);
            INSTANCE_2 = new Singleton2(properties.getProperty("info"));
        } catch (IOException e) {
    
    
            throw new RuntimeException();
        }
    }

    private Singleton2(String info) {
    
    
        this.info = info;
    }
    
    public void setInfo(String info) {
    
    
        this.info = info;
    }

    @Override
    public String toString() {
    
    
        return "Singleton2{info='" + info + "'}";
    }

    public static Singleton2 getInstance(){
    
    
        return INSTANCE_2;
    }
}
//测试:修改了实例的内容后,可以发现,两个变量实际上是指向同一个内存空间的,哈希值也未曾发生改变
public void testSingleton2(){
    
    
    Singleton2 s = Singleton2.getInstance();
    Singleton2 s2 = Singleton2.getInstance();
    System.out.println(s);
    System.out.println(s.hashCode());
    s2.setInfo("abcde");
    System.out.println(s);
    System.out.println(s2.hashCode());
}

Explanation of advantages and disadvantages:

  • Advantages: Singleton classes may need to call variables in other places for instantiation when creating instances, so static code blocks need to be used for processing.
  • Disadvantages: Consistent with 5.4 Hungry-style static constant creation.

Summary : This singleton mode is available, but it may cause memory waste.

5.6 Analysis on whether the hungry Chinese style needs to add final

If there is a situation where resources are released, final modification cannot be added

public void releaseInstance(){
    
     
    if(INSTANCE_1 != null){
    
     
        INSTANCE_1 = null; 
    } 
}

After the resource is released, if the singleton needs to be reused, there must be a reinitialization process, so final cannot be added. For situations where resources do not need to be released, final can be added. All in all, whether to add final modification can be determined according to the situation.
insert image description here

5.7 Lazy style (thread unsafe)

public class Singleton3 {
    
    
    private static Singleton3 instance;

    private Singleton3() {
    
    
    }

    public static Singleton3 getInstance() {
    
    
        if (instance == null) {
    
    
            instance = new Singleton3();
        }
        return instance;
    }
}

Explanation of advantages and disadvantages:

  • Advantages: It has the effect of Lazy Loading lazy loading, but it can only be used under a single thread .
  • Disadvantages: Under multi-threading, if one thread enters the if (instance == null) judgment statement block and has not had time to execute it in the future, another thread also passes the judgment statement, and multiple instances will be generated at this time . So this method cannot be used in a multi-threaded environment.

Summary : In actual development, do not use this method.

5.8 Lazy style (thread-safe, synchronized methods)

public class Singleton4 {
    
    
    private static Singleton4 instance;

    private Singleton4() {
    
    
    }

    public static synchronized Singleton4 getInstance() {
    
    
        if (instance == null) {
    
    
            instance = new Singleton4();
        }
        return instance;
    }
}

Explanation of advantages and disadvantages:

  • Advantages: Solve the problem of thread safety .
  • Disadvantages: Low efficiency . When each thread wants to obtain a class instance, it needs to synchronize when executing the getInstance method. And this method only executes the instantiation code once, and if you want to get an instance of this class later, just return directly. method is too inefficient for synchronization.

Summary : In actual development, this method is not recommended .

5.9 Lazy style (thread-safe, synchronized code blocks)

public class Singleton5 {
    
    
    private static Singleton5 instance;

    private Singleton5() {
    
    
    }

    public static Singleton5 getInstance() {
    
    
        synchronized (Singleton5.class) {
    
    
            if (instance == null) {
    
    
                instance = new Singleton5();
            }
        }
        return instance;
    }
}

Summary : It is actually the same as the synchronization method, and there is no difference.

5.10 Double check

public class Singleton6 {
    
    
    private static volatile Singleton6 instance;

    private Singleton6() {
    
    
    }

    public static synchronized Singleton6 getInstance() {
    
    
        if (instance == null) {
    
    
            synchronized (Singleton6.class) {
    
    
                if (instance == null) {
    
    
                    instance = new Singleton6();
                }
            }
        }
        return instance;
    }
}

Explanation of advantages and disadvantages:

  • advantage:
    • The Double-Check double check concept is often used in multi-threaded development. It can be seen from the code that we have performed two if (instance == null) checks, so that thread safety can be guaranteed.
    • In this way, the instantiation code only needs to be executed once, and when it is accessed again later, judge if (instance == null), directly return the instantiated object, and avoid repeated method synchronization.
    • Thread safety, lazy loading, high efficiency .

Summary : In actual development, it is recommended to use this singleton design pattern.

5.11 Analysis on whether double check needs to add volatile

When a variable is defined as volatile, it will have two characteristics.

  1. Guarantees the visibility of this variable to all threads
    • Explanation : When a thread modifies a volatile variable, it first writes to its working memory, then immediately writes to the main memory, and refreshes the working memory in other threads , so that other threads can read the variables in their working memory , make sure you get the latest one.
  2. Disable instruction reordering optimizations
    • Explanation : Due to the large difference between the execution speed of the CPU and the reading speed of the memory, the computer cannot obtain a breakthrough from the hardware, so the out-of-order execution of the CPU appears. In the case of no dependence on the instruction, the CPU automatically executes the code Optimized to improve the execution efficiency of cpu. Using volatile can hinder the reordering of instructions .

The creation process of instance in JVM: instance=new Singleton6() is not an atomic operation, it is divided into three instruction operations.

  1. Allocate memory to instance
  2. Call the constructor to initialize member variables, forming an instance
  3. Point the instance object to the allocated memory space

After the instructions are reordered, if instructions 1 and 3 are executed first, then instruction 2 is executed. The instance is no longer null, but it has not initialized the instance, so the instance returned by other threads at this time is wrong . So you need to add the volatile keyword.

5.12 Static inner classes

The static inner class is not automatically initialized with the loading and initialization of the outer class, it needs to be loaded and initialized separately.

public class Singleton7 {
    
    
    private Singleton7() {
    
    
    }

    private static class SingletonInstance {
    
    
        private static final Singleton7 INSTANCE_7 = new Singleton7();
    }

    public static Singleton7 getInstance() {
    
    
        return SingletonInstance.INSTANCE_7;
    }
}

Explanation of advantages and disadvantages:

  • advantage:
    • This method uses the mechanism of class loading to ensure that there is only one thread when initializing the instance.
    • The static inner class method does not instantiate the Singleton7 class immediately when it is loaded, but when it needs to be instantiated, calling the getInstance method will load the SingletonInstance class, thereby completing the instantiation of Singleton7.
    • The static properties of the class will only be initialized when the class is loaded for the first time, so here, the JVM helps us ensure the security of the site, and other threads cannot enter when the class is initialized.
    • Thread safety, using the characteristics of static inner classes to implement lazy loading, with high efficiency .

Summary : In actual development, it is recommended to use this singleton design pattern.

5.13 Enumeration

Enumeration type: indicates that objects of this type are limited

We can limit it to one, and it becomes a singleton

public enum Singleton8 {
    
    
    INSTANCE_8;

    public void sayOk() {
    
    
        System.out.println("ok");
    }
}

Explanation of advantages and disadvantages:

  • advantage:
    • This implements the singleton pattern with the help of the enumeration added in JDK1.5. Not only can it avoid multi-thread synchronization problems, but it can also prevent deserialization from recreating new objects.

Summary : In actual development, it is recommended to use this singleton design pattern.

5.14 Source code analysis of singleton mode in JDK application

Runtime in JDK is a classic singleton mode (hungry Chinese style)
insert image description here

5.15 Precautions and details of singleton mode

  1. The singleton mode ensures that there is only one object of this class in the system memory, saving system resources. For some objects that need to be created and destroyed frequently, using the singleton mode can improve system performance.
  2. When you want to instantiate a singleton class, you must remember to use the corresponding method of obtaining the object instead of new
  3. Scenarios for singleton mode : objects that need to be created and destroyed frequently , and it takes too much time or resources to create objects (ieheavyweight object), but frequently used objects,tool object, objects that frequently access databases or files (such asdata sourcesession factorywait)
  4. If it is a hungry Chinese style, the enumeration form is the simplest. If you are lazy, the static inner class form is the simplest

Guess you like

Origin blog.csdn.net/weixin_45930241/article/details/122572601