Design Pattern Learning 2: Singleton Pattern

singleton pattern

The so-called singleton design pattern of a class is to take a certain method to ensure that in the entire software system, only one object instance can exist for a certain class, and the class only provides one method (static method) to obtain its object instance.
Such as Hibernate's SessionFactory, which acts as a proxy for the data storage source and is responsible for creating Session objects. SessionFactory is not lightweight. In general, a project usually only needs one SessionFactory, which is the singleton pattern.

There are eight ways to use the singleton pattern:

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

Hungry Chinese

1. Static constant
Hungry Chinese (static constant) application example The
steps are as follows:

  1. Constructor privatization (to prevent external access to the constructor inside the class, that is, you cannot use the new keyword to create objects)
  2. Objects created inside the class
  3. Expose a static public method. getInstance
  4. Code

example:

/**
 * @author 小关同学
 * @create 2021/9/13
 */
//饿汉式(使用静态变量实现)
class SingleTon{
    
    
    //1、构造器私有化,对外不提供new
    private SingleTon(){
    
    
    }

    //2、本类内部创建对象实例
    private final static SingleTon instance = new SingleTon();

    //3、提供一个公有的静态方法,返回实例对象
    public static SingleTon getInstance(){
    
    
        return instance;
    }
}

public class Type1 {
    
    
    public static void main(String[] args) {
    
    
        SingleTon instance = SingleTon.getInstance();
        SingleTon instance2 = SingleTon.getInstance();
        System.out.println(instance == instance2);//结果是true,证明两个对象相等
    }
}

Analysis of advantages and disadvantages
Advantages: This writing method is relatively simple, that is, the instantiation is completed when the class is loaded. Avoids thread synchronization problems .

Disadvantages: The instantiation is completed when the class is loaded, which does not achieve the effect of Lazy Loading . If the instance is never used from beginning to end, it will cause a waste of memory.

This method avoids the synchronization problem of multiple threads based on the ClassLoder mechanism. However, instance is instantiated when the class is loaded. In the singleton mode, most of them call the getInstance method. However, there are many reasons for class loading, so it is impossible to Make sure that there are other ways (or other static methods) to cause class loading. At this time, initializing the instance will not achieve the effect of lazy loading.

Conclusion: This singleton pattern is available and may cause memory waste

2. Static code block
Example:

//饿汉式(使用静态代码块实现)
class SingleTon{
    
    

    private SingleTon(){
    
    
    }

    private static SingleTon instance;

    //在静态代码块中创建单例对象
    static {
    
    
        instance = new SingleTon();
    }
    
    public static SingleTon getInstance(){
    
    
        return instance;
    }
}

Analysis of advantages and disadvantages
This method is similar to the above method, except that the process of class instantiation is placed in the static code block. When the class is loaded, the code in the static code block is executed to initialize the instance of the class. The pros and cons are the same as above.

Conclusion: This singleton pattern is available, but may cause memory waste

lazy

1. Thread-unsafe writing
Example:

/**
 * @author 小关同学
 * @create 2021/9/13
 */
//懒汉式(线程不安全)
class SingleTon{
    
    
    private static SingleTon instance;

    private SingleTon(){
    
    }

    //提供一个静态的公有方法,当使用到该方法时,才去创建instance
    //即懒汉式
    public static SingleTon getInstance(){
    
    
        if (instance==null){
    
    
            instance = new SingleTon();
        }
        return instance;
    }
}

public class Type2 {
    
    
    public static void main(String[] args) {
    
    
        SingleTon instance = SingleTon.getInstance();
        SingleTon instance2 = SingleTon.getInstance();
        System.out.println(instance == instance2);
    }
}

Analysis of advantages and disadvantages
Advantages: Played the effect of Lazy Loading.

Disadvantage: It can only be used in a single thread . If a thread enters the if (singleton == null) judgment statement block under multi-threading, it will not have time to execute it, and another thread also passes this judgment statement. At this time Multiple instances will be generated.

Conclusion: In actual development, do not use this method

2. Thread-safe, synchronized methods
Example:

//懒汉式(线程安全,同步方法)
class SingleTon{
    
    
    private static SingleTon instance;

    private SingleTon(){
    
    }

    //加入了同步方法,解决了线程不安全的问题
    public static synchronized SingleTon getInstance(){
    
    
        if (instance == null){
    
    
            instance = new SingleTon();
        }
        return instance;
    }
}

public class Type2 {
    
    
    public static void main(String[] args) {
    
    
        SingleTon instance = SingleTon.getInstance();
        SingleTon instance2 = SingleTon.getInstance();
        System.out.println(instance == instance2);
    }
}

Advantages and disadvantages analysis
Advantages: solves the problem of thread insecurity

Disadvantages: The efficiency is too low . When each thread wants to obtain an instance of the class, it needs to synchronize the execution of the getInstance() method. In fact, this method is enough to execute the instantiation code only once. If you want to get an instance of this class later, just return it directly. The method is too inefficient for synchronization

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

3. Thread unsafe, synchronized code blocks
Example:

//懒汉式(线程不安全,同步代码块)
class SingleTon{
    
    
    private static SingleTon instance;

    private SingleTon(){
    
    }
    
    public static SingleTon getInstance(){
    
    
        if (instance == null){
    
    
            //同步代码块
            synchronized (SingleTon.class){
    
    
                instance = new SingleTon();
            }
        }
        return instance;
    }
}

public class Type2 {
    
    
    public static void main(String[] args) {
    
    
        SingleTon instance = SingleTon.getInstance();
        SingleTon instance2 = SingleTon.getInstance();
        System.out.println(instance == instance2);
    }
}

Analysis of advantages and disadvantages
This method is intended to improve the fourth implementation method, because the efficiency of the previous synchronization method is too low, and it is changed to synchronize to generate instantiated code blocks.
But 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 == null) judgment block, and has not yet had time to execute it, another thread also passes the judgment statement, then it will generate multiple instances

Conclusion: In actual development, this method cannot be used.

double check

example:

/**
 * @author 小关同学
 * @create 2021/9/13
 */
//双重检查(线程安全,线程同步)
class SingleTon{
    
    
	//volatile使instance变量的状态在各个线程之间是可见的
    private static volatile SingleTon instance;

    private SingleTon(){
    
    }

    //解决线程安全的问题,同时解决懒加载的问题,同时解决懒加载问题
    public static SingleTon getInstance(){
    
    
        if (instance == null){
    
    
            //同步代码块
            synchronized (SingleTon.class){
    
    
                if (instance == null){
    
    
                    instance = new SingleTon();
                }
            }
        }
        return instance;
    }
}
public class Type3 {
    
    
    public static void main(String[] args) {
    
    
        SingleTon instance = SingleTon.getInstance();
        SingleTon instance2 = SingleTon.getInstance();
        System.out.println(instance == instance2);
    }
}

According to the above code analysis, if three threads a, b, and c enter the getInstance() method, threads a and b enter the first if (instance == null) judgment statement together, and thread a is more advanced than thread b. Enter the synchronization code block first. At this time, since only one thread is allowed to execute in the synchronization code block, the a thread first initializes the instance, and then exits the synchronization code block; at this time, since the a thread exits the synchronization code block, so The b thread can enter the synchronization code block. After the b thread enters the synchronization code block, since the a thread has already initialized the instance, the b thread will not initialize the instance again, and directly exit the synchronization code block; It has been initialized, so it has not entered the judgment statement.

Advantages and disadvantages analysis
Advantages: thread safety, lazy loading, high efficiency

The concept of Double-Check is often used in multi-threaded development. As shown in the above code, we have performed two if (singleton == null) checks to ensure thread safety.
In this way, the instantiation code only needs to be executed once, and when it is accessed again later, judge if (singleton == null), directly return the instantiated object, and avoid repeated method synchronization.

Conclusion: In actual development, this singleton design pattern is recommended.

static inner class

example:

/**
 * @author 小关同学
 * @create 2021/9/13
 */
//静态内部类实现
class SingleTon{
    
    
    private static SingleTon instance;

    private SingleTon(){
    
    }

    //写一个静态内部类,该类中有一个静态属性
    private static class SingleTonInstance{
    
    
        private static final SingleTon INSTANCE = new SingleTon();
    }

    //提供一个公有方法,直接返回SingleTonInstance.INSTANCE
    public static SingleTon getInstance(){
    
    
        return SingleTonInstance.INSTANCE;
    }
}

public class Type4 {
    
    
    public static void main(String[] args) {
    
    
        SingleTon instance = SingleTon.getInstance();
        SingleTon instance2 = SingleTon.getInstance();
        System.out.println(instance == instance2);
    }
}

Code analysis, we wrote a static inner class SingleTonInstance in SingleTon, SingleTonInstance will not be loaded immediately when SingleTon class is loaded, and SingleTonInstance class will not be loaded until the static property INSTANCE in SingleTonInstance class is called in the getInstance() method. It is loaded to achieve the effect of lazy loading, and the process of JVM class loading is thread-safe.

Analysis of advantages and disadvantages
This method adopts the mechanism of class loading to ensure that there is only one thread when the instance is initialized.
The static inner class method will not be instantiated immediately when the Singleton class is loaded, but when it needs to be instantiated, the getInstance method will be called, and the SingletonInstance class will be loaded, thus completing the instantiation of the Singleton.
The static properties of a class are only initialized when the class is loaded for the first time, so here, the JVM helps us ensure thread safety. When the class is initialized, other threads cannot enter.

Advantages: avoid thread insecurity, use static inner class features to achieve delayed loading, high efficiency

Conclusion: Recommended for use.

enumerate

example:

/**
 * @author 小关同学
 * @create 2021/9/13
 */
//枚举(使用枚举也可以实现单例)
enum SingleTon{
    
    
    INSTANCE;   //属性
    public void sayOK(){
    
    
        System.out.println("ok~");
    }
}

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

Analysis of advantages and disadvantages
This implements the singleton pattern with the help of the enumeration added in JDK1.5. Not only does it avoid multi-thread synchronization problems, but it also prevents deserialization from recreating new objects .

This approach is advocated by Effective Java author Josh Bloch

Conclusion: Recommended for use

Application of singleton pattern in JDK

In JDK, java.lang.Runtime is the classic singleton mode (hungry Chinese style). Let's take a look at its source code, as shown below:
insert image description here

Summarize

  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 frequently created and destroyed, 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 to get the object, not new
  3. Scenarios used by the singleton pattern: objects that need to be created and destroyed frequently, time-consuming or resource-consuming (ie: heavyweight objects), but frequently used objects, tool objects, frequent objects Objects that access databases or files (such as data sources, session factories, etc.)


PS: You can also go to my personal blog to see more content

Personal blog address: Xiaoguan classmate's blog

Guess you like

Origin blog.csdn.net/weixin_45784666/article/details/120275606