[Design Pattern 2] - Singleton Pattern

Singleton mode (Singleton): Ensure that there is only one instance of a class and provide a global access point to access it.

Singleton features:

  • A class can only have one instance; (constructor private)
  • It must create this instance by itself; (write the instantiation logic yourself)
  • It must provide this instance to the entire system by itself; (provide an instantiation method externally)

The class diagram is as follows:
insert image description here
the singleton mode is divided into the lazy man style and the hungry man style. In some places, the registered singleton mode is also mentioned. Learn about these three singleton modes.

Hungry Chinese style

public class Person {
    
    
  // 直接创建一个本类的对象
  private static final Person instance = new Person();

  // 构造器私有,外部不能实例化
  private Person() {
    
    
  }

  public static Person getInstance() {
    
    
    return instance;
  }
}

Lazy

public class Person {
    
    
  private static Person instance;

  // 构造器私有,外部不能实例化
  private Person() {
    
    
  }
  public static Person getInstance() {
    
    
    // 如果没有实例再去创建
    if (instance == null) {
    
    
      Person person = new Person();
      instance = person;
    }
    return instance;
  }
}

Using the lazy style under multi-threading will destroy the singleton mode.

method locking

public class Person {
    
    
  private static Person instance;

  // 构造器私有,外部不能实例化
  private Person() {
    
    
  }

  // 单例提供给外部的获取方法
  // 1. public static synchronized Person getInstance() 锁太大导致效率低下
  public static synchronized Person getInstance() {
    
    
    // 如果没有实例再去创建
    if (instance == null) {
    
    
      Person person = new Person();
      instance = person;
    }
    return instance;
  }
}

Directly adding the method of obtaining an instance synchronizedcan solve the problem under multi-threading, but this method will lead to low efficiency.

double checked lock + volatile

 private volatile static Person instance;
  // 构造器私有,外部不能实例化
  private Person() {
    
    
  }

  // 单例提供给外部的获取方法
  // 1. 双重检查锁 + 内存可见性
  public static synchronized Person getInstance() {
    
    
    // 如果没有实例再去创建
    if (instance == null) {
    
    
      synchronized (Person.class) {
    
    
        if (instance == null) {
    
    
          Person person = new Person();
          instance = person;
        }
      }
    }
    return instance;
  }

Using double-checked locks can improve efficiency. Using volatile can lead to instruction reordering.

Prevent serialization from breaking the singleton pattern

After a singleton object is created, sometimes it is necessary to serialize the object and then write it to disk, and then read the object from disk and deserialize it next time it is used, and convert it into a memory object. The deserialized object will re-allocate memory, that is, recreate it. If the target of the serialized object is a singleton object, it will violate the original intention of the singleton pattern, which is equivalent to destroying the singleton.

Solution: readResolve()Just override the method in the class
insert image description here

Prevent reflection from breaking the singleton pattern

Hungry Chinese style

If it is a Hungry Chinese singleton pattern, directly judge whether the instance is in the constructor null, the code is as follows:

public class Person {
    
    
  private volatile static Person instance = new Person();

  // 构造器私有,外部不能实例化
  private Person() {
    
    
    if (instance != null) {
    
    
      throw new RuntimeException("单例不允许多实例");
    }
  }

  public static Person getInstance() {
    
    
    return instance;
  }

Lazy

Add a flag to prevent reflection destruction

public class Person {
    
    
  private volatile static Person instance;
  private static boolean flag = false;

  // 构造器私有,外部不能实例化
  private Person() {
    
    
    synchronized (Person.class) {
    
    
      if (!flag) {
    
    
        flag = true;
      } else {
    
    
        throw new RuntimeException("单例不允许多实例");
      }
    }
  }


  public static synchronized Person getInstance() {
    
    
    // 如果没有实例再去创建
    if (instance == null) {
    
    
      synchronized (Person.class) {
    
    
        if (instance == null) {
    
    
          Person person = new Person();
          instance = person;
        }
      }
    }
    return instance;
  }
}

In fact, the flag bit cannot prevent reflection from destroying the singleton mode, and the singleton mode can be implemented through the enumeration class to prevent reflection from breaking.

Guess you like

Origin blog.csdn.net/qq_60361946/article/details/128581604