[Design Mode]-Singleton Mode

1. Introduction to Singleton Mode

1. What is the singleton pattern

Singleton mode (singleton ) refers to ensuring that a class has only one instance and providing a global access point through code . In fact, generally speaking, a class that implements the singleton pattern design should only be initialized once and provide a method so that all users can obtain this instance.

2. Application scenarios of singleton mode

In the book Design Patterns, it is mentioned that the singleton pattern is applicable to two scenarios:

  1. When the class can only have one instance and clients can access it from a well-known access point.
  2. When this unique instance should be extensible through subclassing, and the coder should be able to use an extended instance without changing the code.

Simply put, if an entity and its intrinsic properties are needed by all classes and its intrinsic properties will not change or the changes are common to all classes, this class can be considered as a singleton mode. The advantage of the singleton mode is that the class has only one instance, which reduces the overhead of system performance and optimizes the problem of shared resource access. For example, our resource loading-related classes are time-consuming and do not need to be created. This kind of class is suitable for creation in singleton mode.

Second, the realization of the singleton pattern

Here I am going to introduce the implementation of several singleton modes and simply analyze the advantages and disadvantages of each implementation. If you don’t understand the cuteness, you can leave a message or private message in the comment area, and I will answer for you as soon as possible.

1. Hungry Chinese

public class HungrySingleton {
    
    

	private static final HungrySingleton SINGLETON = new HungrySingleton();

	private HungrySingleton() {
    
    
		super();
		// TODO Auto-generated constructor stub
	}

	public static HungrySingleton getInstance() {
    
    
		return SINGLETON;
	}
}

Design idea:

  1. Make the constructor of the class private
  2. The class that needs to implement a singleton is used as a member variable and decorated with final
  3. Provide a method getInstance that can access an instance of the class

**Advantages:** No locks, high execution efficiency.
** Disadvantages: ** Initialize when the class is loaded, which will take up space and waste memory.

2. Lazy man

public class LazySingleton {
    
    

	private static LazySingleton LAZY_SINGLETON;

	public static LazySingleton getInstance() {
    
    
		if (LAZY_SINGLETON == null) {
    
    
			LAZY_SINGLETON = new LazySingleton();
		}
		return LAZY_SINGLETON;
	}

	private LazySingleton() {
    
    
		super();
		// TODO Auto-generated constructor stub
	}

}

Design idea:

  1. Make the constructor of the class private
  2. Provide member variables of the current class
  3. Provide the getInstance method that can obtain the current instance, in the method to determine whether the class has been instantiated, if not instantiated, instantiate, if it has been instantiated, directly return to the instance of the class.

Advantages: only go home when the class is used for the first time, saving memory space.
Disadvantages: The getInstance method is not atomic and will cause thread safety issues.

3. DCL

The full name of DCL is called double check lock method

public class DCLSingleton {
    
    
	private static volatile DCLSingleton dclSingleton;

	public static DCLSingleton getInstance() {
    
    
		if (dclSingleton == null) {
    
    
			synchronized (DCLSingleton.class) {
    
    
				if (dclSingleton == null) {
    
    
					dclSingleton = new DCLSingleton();
				}
			}
		}
		return dclSingleton;
	}
	private DCLSingleton() {
    
    
		super();
		// TODO Auto-generated constructor stub
	}
}

Realization ideas:

Compared with the hungry Chinese style, the DCL singleton mode first adds the volatile keyword modification to the member variables to prevent semi-initialization problems caused by instruction reordering.
In the getInstance method, the synchronization code modified by synchronized is added to ensure the atomicity of the code block operation and solve the thread safety problem.
Make another judgment inside the synchronized code block to ensure thread safety.

Advantages : lazy loading, saving memory space. Fast modification of the synchronization code ensures thread safety.
Disadvantages : The use of locks will affect the user experience.

4. Static inner class

public class StaticSingleton {
    
    

	private static class Singleton {
    
    
		private static final StaticSingleton SINGLETON = new StaticSingleton();
	}

	public static StaticSingleton getInstance() {
    
    
		return Singleton.SINGLETON;
	}

	private StaticSingleton() {
    
    
		super();
		// TODO Auto-generated constructor stub
	}

}

Realization ideas:

Through the static inner class, it will be loaded when the class is instantiated

Advantages : lazy loading is implemented, no locks are used.
Disadvantages : no obvious disadvantages

5. Enumeration

public enum EnumSingleton {
    
    
	instance;
}

Design ideas:

Effective java author recommends the singleton implementation, all enumerations can be understood as public static final modification.

Advantages: Simple writing, thread safe.
Disadvantages: Hungry Chinese loading

Three, destroy the singleton mode

The often-said singleton writing method has not yet been fully realized. In fact, we can destroy the specificity of the singleton pattern itself through some behaviors. Therefore, if we do not do specific processing, the singleton pattern we write is to guard against the gentleman but not the villain. Here I give a simple example of how to break the singleton pattern and solutions.

1. Reflection destruction singleton mode

We add a main method to the hungry Chinese code above for testing

	public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException,
			IllegalAccessException, IllegalArgumentException, InvocationTargetException {
    
    

		HungrySingleton instance = HungrySingleton.getInstance();
		Class<?> hun = HungrySingleton.class;
		Constructor ctr = hun.getDeclaredConstructor(null);
		ctr.setAccessible(true);
		HungrySingleton newInstance = (HungrySingleton) ctr.newInstance();
		System.out.println(instance.hashCode());
		System.out.println(newInstance.hashCode());
	}

Here we can observe the printing result:
Insert picture description here
we can see that we destroyed the singleton mode after reflection, and the solution is also very simple, just need to do the corresponding processing in the constructor.

public class HungrySingleton {
    
    

	private static final HungrySingleton SINGLETON = new HungrySingleton();

	private HungrySingleton() {
    
    
		if (SINGLETON != null) {
    
    
			throw new RuntimeException("不允许创建多个实例");
		}
	}
	public static HungrySingleton getInstance() {
    
    
		return SINGLETON;
	}
}

operation result:
Insert picture description here

2. Deserialization destroys singleton mode

After an object is created, sometimes it is necessary to serialize the object and then write it to disk, and then read the object from the disk and deserialize it when it is used next time, and convert it to a memory object. The memory address will be re-allocated, that is, re-created. If the target of deserialization is a singleton object, it violates the principle of the singleton pattern. Let's take a look at an example:
First let the hungry man implement the Serializable interface

public class HungrySingleton implements Serializable {
    
    

	private static final HungrySingleton SINGLETON = new HungrySingleton();

	private HungrySingleton() {
    
    
	}

	public static HungrySingleton getInstance() {
    
    
		return SINGLETON;
	}
}

Then write the test main method:

	public static void main(String[] args) {
    
    

		HungrySingleton instance01 = HungrySingleton.getInstance();
		HungrySingleton instance02 = null;
		FileOutputStream fos = null;
		try {
    
    
			fos = new FileOutputStream("HungrySingleton.obj");
			ObjectOutputStream oos = new ObjectOutputStream(fos);
			oos.writeObject(instance01);
			oos.flush();
			oos.close();

			FileInputStream fis = new FileInputStream("HungrySingleton.obj");
			ObjectInputStream objectInputStream = new ObjectInputStream(fis);
			instance02 = (HungrySingleton) objectInputStream.readObject();
			objectInputStream.close();
			System.out.println(instance01.hashCode());
			System.out.println(instance02.hashCode());
		} catch (Exception e) {
    
    
			// TODO: handle exception
		}

	}

Operation result:
Insert picture description here
Solution:
Just add the readResolve() method

public class HungrySingleton implements Serializable {
    
    

	private static final HungrySingleton SINGLETON = new HungrySingleton();

	private HungrySingleton() {
    
    

	}

	public static HungrySingleton getInstance() {
    
    
		return SINGLETON;
	}

	private Object readResolve() {
    
    
		return SINGLETON;
	}
}

Operation result: The
Insert picture description here
reason is that the objectInputStream.readObject() method uses reflection to find the readResolve method in the entity to be reflected during deserialization. If not, it will call the class constructor.

Concluding remarks

Well, the content related to the singleton mode has been sorted out. I hope that the rewarding friends can get three links with one click, and don't be a idiot!

Guess you like

Origin blog.csdn.net/xiaoai1994/article/details/112217525