java Interview Common Pattern Questions Singleton Pattern

  • 2021-09-12 01:04:10
  • OfStack

Catalogue 1, Introduction 2, Singleton Mode-Lazy Type 3, Singleton Mode-Hungry Type Summary

1. Introduction

Singleton Mode Usage Scenario:

The business system globally requires only one object instance, such as the numberer, redis connection object, and so on. Spring IOC Bean in the container is singleton by default. Through Controller, Service and Dao layers in Spring Boot @Autowire Dependency note objects of are singleton by default.

Singleton pattern classification:

Lazy: It is called lazy loading, which delays the creation of objects and creates objects when needed. Hungry man: Contrary to lazy man, create objects in advance. Singleton pattern implementation steps: Privatization constructor. Provides methods to get singletons.

2. Singleton Mode-Lazy Style

Singleton mode-lazy mode has the following implementation modes:

/**
 * @Auther: csp1999
 * @Date: 2020/11/06/20:36
 * @Description: 单例设计模式-懒汉式
 */
public class SingletonLazy {
    // 当需要用到该实例的时候再创建实例对象
    private static SingletonLazy instance;
    /**
     * 构造函数私有化
     * 不能通过 new SingletonLazy() 的方式创建实例
     * 
     * 当需要用到该实例的时候在加载
     * 只能通过 SingletonLazy.getInstance() 这种方式获取实例
     */
    private SingletonLazy() {
    }
    /**
     * 单例对象的方法
     */
    public void process() {
        System.out.println("方法实例化成功!");
    }
    /**
     * 方式1:
     * <p>
     * 对外暴露1个方法获取该类的对象
     * <p>
     * 缺点:线程不安全,多线程下存在安全问题
     *
     * @return
     */
    public static SingletonLazy getInstance() {
        if (instance == null) {// 实例为null时候才创建
            /**
             * 线程安全问题:
             * 当某1时刻,两个或多个线程同时判断到instance == null成立的时候
             * 这些线程同时进入该if判断内部执行实例化
             * 则会新建出不止1个SingletonLazy实例
             */
            instance = new SingletonLazy();// 当需要的时候再进行实例化对象
        }
        return instance;
    }
    /**
     * 方式2:
     * 通过加synchronized锁 保证线程安全
     *
     * 采用synchronized 对方法加锁有很大的性能开销
     * 因为当getInstance2()内部逻辑比较复杂的时候,在高并发条件下
     * 没获取到加锁方法执行权的线程,都得等到这个方法内的复杂逻辑执行完后才能执行,等待浪费时间,效率比较低
     *
     * @return
     */
    public static synchronized SingletonLazy getInstance2() {
        if (instance == null) {// 实例为null时候才创建
            // 方法上加synchronized锁后可以保证线程安全
            instance = new SingletonLazy();// 当需要的时候再进行实例化对象
        }
        return instance;
    }
    /**
     * 方式3:
     * 在getInstance3()方法内,针对局部需要加锁的代码块加锁,而不是给整个方法加锁
     *
     * 也存在缺陷:
     * @return
     */
    public static SingletonLazy getInstance3() {
        if (instance == null) {// 实例为null时候才创建
            // 局部加锁后可以保证线程安全,效率较高
            // 缺陷:假设线程A和线程B
            synchronized (SingletonLazy.class){
                // 当线程A获得锁的执行权的时候B等待 A执行new SingletonLazy();实例化
                // 当A线程执行完毕后,B再获得执行权,这时候还是可以实例化该对象
                instance = new SingletonLazy();// 当需要的时候再进行实例化对象
            }
        }
        return instance;
    }
}
Singleton mode: lazy implementation + double-checked locking + memory model

For the defects of Mode 3 above, we can use double-check locking to improve it:


/**
 *  Mode 3 Improved version: 
 *  In getInstance3() Method, instead of locking the whole method, lock the code block that needs to be locked locally 
 *
 * DCL  Double check locking  (Double-Checked-Locking)  Maintain high performance in multithreaded situations 
 *
 *  Is this safe?  instance = new SingletonLazy();  Is not an atomic operation 
 * jvm Medium  instance The process of instantiating the memory model is as follows: 
 * 1. Allocate space to objects 
 * 2. Create an object in a space 
 * 3. Assign an object to instance Quote 
 *
 *  If the following sequence is out of order: 
 *  The order in which threads are executed is: 1 -> 3 -> 2,  Then the value will be written back to main memory at this time 
 *  Then, other threads will read the instance Latest value of , But this is an incomplete object 
 * ( Instruction rearrangement phenomenon )
 *
 * @return
 */
public static SingletonLazy getInstance3plus() {
    if (instance == null) {//  The instance is null Is created when 
        //  Partial locking can ensure thread safety , High efficiency 
        //  Assuming threads A And threads B 
        synchronized (SingletonLazy.class){//  No. 1 1 Re-check 
            //  When threads A When you get the right to execute the lock, B Wait  A Execute new SingletonLazy(); Instantiation 
            //  When A When the thread finishes executing, B Get the right to execute again, and then judge again instance == null Whether it is true or not 
            //  If it doesn't hold true, B Thread cannot   Instantiation SingletonLazy
            if (instance == null){//  No. 1 2 Re-check 
                instance = new SingletonLazy();//  Instantiate objects when needed 
            }
        }
    }
    return instance;
}

Upgrade Mode 3 again to solve the instruction rearrangement problem in the memory model:


//  Add volatile  Keyword, which prohibits instruction rearrangement in the memory model when instantiating an object 
private static volatile SingletonLazy instance;
/**
 *  Mode 3 Upgrade version again: 
 *  In getInstance3() Method, instead of locking the whole method, lock the code block that needs to be locked locally 
 *
 * DCL  Double check locking  (Double-Checked-Locking)  Maintain high performance in multithreaded situations 
 *
 *  Solve the problem of instruction rearrangement-prohibit instruction rearrangement 
 * @return
 */
public static SingletonLazy getInstance3plusplus() {
    if (instance == null) {//  The instance is null Is created when 
        //  Partial locking can ensure thread safety , High efficiency 
        //  Assuming threads A And threads B
        synchronized (SingletonLazy.class){//  No. 1 1 Re-check 
            //  When threads A When you get the right to execute the lock, B Wait  A Execute new SingletonLazy(); Instantiation 
            //  When A When the thread finishes executing, B Get the right to execute again, and then judge again instance == null Whether it is true or not 
            //  If it doesn't hold true, B Thread cannot   Instantiation SingletonLazy
            if (instance == null){//  No. 1 2 Re-check 
                instance = new SingletonLazy();//  Instantiate objects when needed 
            }
        }
    }
    return instance;
}

Singleton mode-lazy call:


@Test
public void testSingletonLazy(){
    SingletonLazy.getInstance().process();
}

3. Single case mode-hungry Han style


/**
 * @Auther: csp1999
 * @Date: 2020/11/06/21:39
 * @Description:  Singleton design pattern - Hungry Han style 
 */
public class SingletonHungry {
    //  Instantiate the object directly when the class is loaded 
    private static SingletonHungry instance = new SingletonHungry();
    private SingletonHungry(){}
    /**
     *  Methods of singleton objects 
     */
    public void process() {
        System.out.println(" Method instantiated successfully! ");
    }
    public static SingletonHungry getInstance(){
        return instance;//  Instantiate the object directly when the class is loaded 
    }
}
Singleton mode-hungry call;

@Test
public void testSingletonHungry(){
    SingletonHungry.getInstance().process();
}

Hungry singleton mode instantiates the object directly when the class is loaded, so there is no need to consider thread safety.

Advantages: Simple implementation, no need to consider thread safety issues. Disadvantages: instance Object 1 takes up this memory with or without the object instance.

How to choose between lazy man and hungry man?

If the object memory occupation is not large and the creation is not complicated, it can be directly used in a hungry way. In other cases, lazy people are used (preferred).

Summarize


Related articles: