JAVA design pattern -- eight ways of singleton pattern

catalogue

Introduction to singleton mode:

Advantages of singleton mode:

Application scenario:

There are eight ways of singleton design mode:

1. Hungry Han formula (static constant)

2. Hungry Chinese (static code block)

3. Lazy (thread unsafe)

4. Lazy (thread safety, synchronization method)

5. Lazy (thread safe, synchronous code block)

6. Double check (recommended)

7. Static inner class (recommended)

8. Enumeration (recommended)

Source code analysis of singleton pattern in JDK application

Notes and details of singleton mode

Introduction to singleton mode:

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.

For example, Hibernate's SessionFactory acts as a proxy for the data storage source and is responsible for creating the Session object. SessionFactory is not lightweight. Generally, only one SessionFactory is required for a project, which will use the singleton mode.

Advantages of singleton mode:

  1. Since it has only one object instance in memory, memory space is saved
  2. It can avoid frequent creation and destruction of objects and improve performance
  3. Avoid multiple occupation of shared resources and simplify access operations. For example, when writing files, because there is only one instance object, it can avoid writing to the same resource at the same time
  4. Provide a global access point for the whole system to optimize and share resource access.

Application scenario:

The core of singleton mode is to create only one instance in the whole system. Its application scenarios are mainly as follows:

1. Objects that take too much time or resources to create but are often used.
2. Objects that need to be instantiated frequently and then destroyed. Such as multi-threaded thread pool, network connection pool, etc.
3. When objects need to be shared. Since singleton mode allows only one object to be created, sharing the object can save memory and speed up object access. Such as the configuration object in the Web, the connection pool of the database, etc.
4. For some classes that need to be created frequently, using singleton can reduce the memory pressure of the system and reduce GC.
5. Counter of website (otherwise it is difficult to synchronize)
6.Windows Task Manager and recycle bin.

There are eight ways of singleton design mode:

1. Hungry Han formula (static constant)

Application example:

1. Privatization of constructor (prevent new)

2. Create objects inside the class

3. Expose a static public method: getInstance

4. Code implementation

public class SingletonTest01 {
    public static void main(String[] args) {
        //test
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance1=instance1);    //true
        System.out.println("instance1.hsashCode="+instance1.hashCode());
        System.out.println("instance2.hsashCode="+instance2.hashCode());
    }
}
//Hungry Han style is hungry as the name suggests, so you should create objects from the beginning
class Singleton{
    // 1. The constructor is privatized, and the external cannot be new
    private Singleton(){
    }
    // 2. Create object instances within this class
    private final static Singleton instance=new Singleton();
    // 3. Provide a public static method to return the instance object
    public static Singleton getInstance(){
        return instance;
    }
}

Operation results:

  Description of advantages and disadvantages:
1. Advantages: this method is relatively simple, that is, instantiation is completed when the class is loaded. Thread synchronization problems are avoided.
2. Disadvantages: the instantiation is completed when the class is loaded, which does not achieve the effect of Lazy Loading. If this instance is never used from beginning to end, it will cause a waste of memory
3. This method is based on the classloder mechanism to avoid the problem of multi-threaded synchronization. However, instance is instantiated during class loading. In the singleton mode, most of them call the getInstance method, but there are many reasons for class loading, so it is uncertain that there are other methods (or other static methods) cause class loading. At this time, initializing instance does not achieve the effect of lazy loading
Conclusion: This singleton mode is available, but it may cause memory waste

2. Hungry Chinese (static code block)

class Singleton01{
    // 1. The constructor is privatized, and the external cannot be new
    private Singleton01(){
    }
    // 2. Create object instances within this class
    private  static Singleton01 instance;
    static {  //In a static code block, create a singleton object
        instance=new Singleton01();
    }
    // 3. Provide a public static method to return the instance object
    public static Singleton01 getInstance(){
        return instance;
    }
}

Advantages and disadvantages: like the above, this method is actually similar to the above method, except that the class instantiation process is placed in the static code block, that is, when the class is loaded, the code in the static code block is executed to initialize the class instance.

3. Lazy (thread unsafe)

public class SingletonTest03 {
    public static void main(String[] args) {
        //test
        System.out.println("Lazy, thread unsafe~");
        Singleton03 instance1 = Singleton03.getInstance();
        Singleton03 instance2 = Singleton03.getInstance();
        System.out.println(instance1==instance2);    //true
        System.out.println("instance1.hsashCode="+instance1.hashCode());
        System.out.println("instance2.hsashCode="+instance2.hashCode());
    }
}
//As the name suggests, the lazy type is lazy. It will not create objects just like the hungry type, but will be created when needed, and only once
class Singleton03{
    // 1. The constructor is privatized, and the external cannot be new
    private Singleton03(){
    }
    // 2. Create object instances within this class
    private  static Singleton03 instance;

    // 3. Provide a public static method. When this method is used, instance will be created
    //Lazy style
    public static Singleton03 getInstance(){
        if(instance==null){
            instance=new Singleton03();
        }
        return instance;
    }
}

Operation results:

Description of advantages and disadvantages:
1. It has the effect of Lazy Loading, but it can only be used under single thread.
2. If a thread enters the if (singleton == mull) judgment statement block and has not yet had time to execute, and another thread also passes the judgment statement, multiple instances will be generated. Therefore, this method cannot be used in a multithreaded environment
3. Conclusion: do not use this method in actual development

4. Lazy (thread safety, synchronization method)

class Singleton04{
    // 1. The constructor is privatized, and the external cannot be new
    private Singleton04(){
    }
    // 2. Create object instances within this class
    private  static Singleton04 instance;
    // 3. Provide a public static method and add synchronous processing code to solve thread safety problems
    //Lazy style
    public static synchronized  Singleton04 getInstance(){
        if(instance==null){
            instance=new Singleton04();
        }
        return instance;
    }
}

Operation results:

Description of advantages and disadvantages:
1. Solves thread safety issues
2. The efficiency is too low. When each thread wants to obtain an instance of a class, it needs to synchronize by executing the getInstance() method. In fact, this method only executes the instantiation code once, and the later ones just return if they want to obtain an instance of the class. The efficiency of method synchronization is too low
3. Conclusion: this method is not recommended in practical development

5. Lazy (thread safe, synchronous code block)

class Singleton05{
    // 1. The constructor is privatized, and the external cannot be new
    private Singleton05(){
    }
    // 2. Create object instances within this class
    private  static Singleton05 instance;
    // 3. Provide a public static method and add synchronous code blocks
    public static Singleton05 getInstance(){
        if(instance==null){
          synchronized (Singleton05.class){
              instance=new Singleton05();
          }
        }
        return instance;
    }
}

Description of advantages and disadvantages:

1. This method is intended to improve the fourth implementation method. Because the efficiency of the previous synchronization method is too low, the instantiated code block is generated synchronously
2. However, this synchronization does not play the role of thread synchronization. Consistent with the situation encountered in the third implementation method, if a thread enters the if (singleton == nul) judgment statement block and has not had time to execute down, another thread also passes the judgment statement, multiple instances will be generated
Conclusion: this method cannot be used in practical development

6. Double check (recommended)

//duplication check
class Singleton06{
    // 1. The constructor is privatized, and the external cannot be new
    private Singleton06(){
    }
    // 2. Create object instances within this class
    private  static volatile Singleton06 instance;
    // 3. Provide a public static method and add double check code to solve the thread safety problem and lazy loading problem
    public static synchronized Singleton06 getInstance(){
        if(instance==null){
          synchronized (Singleton06.class){
              if(instance==null){
                  instance=new Singleton06();
              }
          }
        }
        return instance;
    }
}

Operation results:

Description of advantages and disadvantages:

1. The concept of double check is often used in multithreading development. As shown in the code, we have conducted two if (singleton==- null) checks to ensure thread safety.
2. In this way, the instantiated code is executed only once. When accessing again later, judge if (singleton ==null) and return the instantiated object directly to avoid repeated method synchronization
3. Thread safety; Delayed loading; High efficiency
4. Conclusion: this single case design pattern is recommended in practical development

Why must volatile be used?

In the java memory model, the volatile keyword is used to:

  1. Ensure memory visibility of variable operations by different threads
  2. Prohibit instruction reordering

7. Static inner class (recommended)

//The static internal class is completed, which is recommended
class Singleton07{
    // 1. Privatization of constructor
    private Singleton07(){
    }
    // 2. Create object instances within this class
    private  static volatile Singleton07 instance;
    //Write an internal static class that has a static property
    private static class SingletonInstance{
        private static final Singleton07 INSTANCE=new Singleton07();
    }
    // 3. Provide a public static method to directly return SingletonInstance.INSTANCE
    public static synchronized Singleton07 getInstance(){

        return SingletonInstance.INSTANCE;
    }
}

Operation results:

Description of advantages and disadvantages:
1. This method adopts the mechanism of class loading to ensure that there is only one thread when initializing the instance.  

2. The static internal class method will not be instantiated immediately when the Singleton class is loaded. Instead, when instantiation is required, the SingletonInstance class will be loaded by calling the getInstance method, so as to complete the instantiation of Singleton.
3. The static properties of the class will only be initialized when the class is loaded for the first time. Therefore, the JVM helps us ensure the safety of threads. When the class is initialized, other threads cannot enter.
4. Advantages: thread insecurity is avoided, delayed loading is realized by using the characteristics of static internal classes, and the efficiency is high

8. Enumeration (recommended)

public class SingletonTest08 {
    public static void main(String[] args) {
        Singleton08 instance = Singleton08.INSTANCE;
        Singleton08 instance2 = Singleton08.INSTANCE;
        System.out.println(instance==instance2);

        System.out.println(instance.hashCode());
        System.out.println(instance2.hashCode());
        instance.sayOk();
    }
}
//Using enumeration, you can implement singleton. It is recommended
enum Singleton08{
    INSTANCE;//attribute
    public void sayOk(){
        System.out.println("ok~");
    }
}

Operation results:

  Advantages and disadvantages:

1. This implements the singleton pattern with the help of enumerations added in JDK 1.5. It can not only avoid the problem of multi-threaded synchronization, but also prevent deserialization and re creation of new objects.
2. This approach is advocated by Josh Bloch, author of Effective Java

3. Conclusion: Recommended

Source code analysis of singleton pattern in JDK application

In our JDK, java.lang.Runtime is a classic singleton pattern (hungry Chinese)
As can be seen from the following source code:

Notes 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 the system performance
2. When you want to instantiate a singleton class, you must remember to use the corresponding method to get the object, not new

3. It is not recommended to use reflection to design singleton mode, because reflection can violently destroy singleton mode

Tags: Java

Posted on Fri, 15 Oct 2021 22:10:49 -0400 by kujtim