Understanding the Singleton Pattern

1. Function

single instance

Guarantee that our instance object has only one instance in the whole application

2. How to create

Five ways to achieve

2.1 Hungry Chinese style

Immediately initialize when the class is loaded, and create a singleton object
It is absolutely thread-safe, it is instantiated before the thread appears, and there is no access security problem

Advantages: no locks are added, the execution efficiency is relatively high, and the user experience is better than the lazy singleton mode
Disadvantages: waste of memory, occupying memory whether it is used or not

public class HungrySingleton {
// direct instantiation
// private static final HungrySingleton hungrySingleton = new HungrySingleton();
private static final HungrySingleton hungrySingleton;
// static block singleton pattern
static {
hungrySingleton = new HungrySingleton();
}
private HungrySingleton(){}
private static HungrySingleton getInstance(){
return hungrySingleton;
}
}

2.2 The Lazy Man

It is only instantiated when it is called externally, which avoids wasting resources compared to the hungry Chinese style.
In the case of single thread, it is more friendly.

In the case of multi-threading, there will be thread safety issues

public class LazySimpleSingleton {
private LazySimpleSingleton() {
}
private static LazySimpleSingleton lazySimpleSingleton = null;
public static LazySimpleSingleton getInstance() {
if (null == lazySimpleSingleton) {
lazySimpleSingleton = new LazySimpleSingleton();
}
return lazySimpleSingleton;
}
}
2.3 Double Detect Lock (DCL)

Based on the lazy thread safety problem, there is a double check lock

public class LazyDoubleCheckSingleton {
private volatile static LazyDoubleCheckSingleton lazyDoubleCheckSingleton = null;
/**
* Private constructor to prevent instantiation directly through new
*/
private LazyDoubleCheckSingleton() {
}
public static LazyDoubleCheckSingleton getInstance() {
if (lazyDoubleCheckSingleton == null) {
synchronized (LazyDoubleCheckSingleton.class) {
if (lazyDoubleCheckSingleton == null) {
// 1. Allocate memory space 2. Execute the constructor, instantiate the object 3. Assign the object to this space
// Without the volatile keyword, it will cause instruction rearrangement, 1,3,2
lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton();
}
}
}
return lazyDoubleCheckSingleton;
}
}

2.4 Static inner classes

public class StaticSingleton {
public static class InnerStaticSingleton {
/**
* Declare static constants of outer types
*/
public static final StaticSingleton instance = new StaticSingleton();
}

private StaticSingleton() {
}
public StaticSingleton getInstance() {
return InnerStaticSingleton.instance;
}
}

2.5 Enumeration Types

public enum EnumSingleton {
INSTANCE;
public void handleMethod(){
// business processing
}
}

In summary, most of the five ways of writing are considering thread safety issues.

2.6 Reflection blasting problem

A private constructor that can be destroyed by reflection.

The judgment is made in the private constructor, and an exception is thrown.

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
LazySimpleSingleton instance = LazySimpleSingleton.getInstance();
Class clazz = LazySimpleSingleton.class;
Constructor constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
LazySimpleSingleton instance1 = constructor.newInstance();
System.out.println(instance);
System.out.println(instance1);
}

2.7 Serialization and deserialization destroy singletons

LazySimpleSingleton to implement Serializable serialization interface

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException, ClassNotFoundException {
LazySimpleSingleton instance = LazySimpleSingleton.getInstance();
Class clazz = LazySimpleSingleton.class;
Constructor constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
LazySimpleSingleton instance1 = constructor.newInstance();
System.out.println(instance);
System.out.println(instance1);
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("d:/tools/a.txt"));
outputStream.writeObject(instance);
outputStream.flush();
outputStream.close();
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("d:/tools/a.txt"));
LazySimpleSingleton instance2 = (LazySimpleSingleton) inputStream.readObject();
inputStream.close();
System.out.println(instance);
System.out.println(instance2);
}

We need to override the readResolve() method
private Object readResolve() {
return lazySimpleSingleton;
}

Note: The readResolve() method is based on callbacks. During deserialization, if readResolve() is defined, the object specified by this method is directly returned without creating a new object.

3. Application

The singleton pattern seen in the framework

1. Bean objects in Spring, the default is singleton mode
2. The related factory objects are all singletons, such as: SqlSessionFactory in Mybatis, BeanFactory in Spring
3. All the related configuration information is stored in singletons, such as: Configuration object in Mybatis, each xxxAutoConfiguration object in SpringBoot
4. The log application of the application is generally implemented through a singleton
5. The design of the database connection pool is also a singleton mode

Related Articles

Explore More Special Offers

  1. Short Message Service(SMS) & Mail Service

    50,000 email package starts as low as USD 1.99, 120 short messages start at only USD 1.00

phone Contact Us