This is the 20th day of my participation in the August Text Challenge.More challenges in August

Singleton Pattern is one of the simplest design patterns in Java. This type of design Pattern is the creation Pattern, which provides an optimal way to create objects.

The singleton pattern is most commonly used in real-world development. For frequently used classes, we can define it as a singleton to avoid frequent creation and destruction of class objects and improve efficiency. The singleton pattern has the following characteristics:

  • A singleton class can have only one instance
  • A singleton class must create its own unique instance
  • The singleton class provides this instance to other objects

A UML diagram of the singleton pattern

Based on the characteristics of the singleton pattern, the common implementation methods are as follows:

  • LanHanShi
  • The hungry type
  • Double Check Lock (DCL) Double Check Lock
  • Static inner class implementation
  • Enumerated the singleton
  • Record singleton

Lazy lazy declares a static class object and then initializes it the first time the user calls the initialization method.

The drawback of lazy threading is that it cannot guarantee only one instance in multi-threaded conditions. So strictly speaking, in a multi-threaded environment, you can’t call it a singleton pattern.

/*** * lazy *@author Iflytek_dsw
 *
 */
public class Singleton {
	private static Singleton instance;
	private Singleton(a){};
	
	public static Singleton getInstance(a){
		if(instance == null){
			instance = new Singleton();
		}
		returninstance; }}Copy the code

Lazy thread-safety achieves thread synchronization by adding a synchronized method lock to the getInstance method, which is synchronized every time it is called, which is inefficient in a single-threaded environment.

/*** * lazy thread safety *@author Iflytek_dsw
 *
 */
public class Singleton {
	private static Singleton instance;
	private Singleton(a){};
	
	public static synchronized Singleton getInstance(a){
		if(instance == null){
			instance = new Singleton();
		}
		returninstance; }}Copy the code

The hander is instantiated when the singleton is declared.

/*** * Hungry *@author Iflytek_dsw
 *
 */
public class Singleton {
	private static Singleton instance = new Singleton();
	private Singleton(a){};
	
	public static synchronized Singleton getInstance(a){
		returninstance; }}Copy the code

This approach avoids the problem of executing getInstance in a multithreaded environment, but it is possible to have different instances in different classloaders.

DCL mode (double-checked locking) Double-checked locking is implemented by determining whether instance is null multiple times and then using the volatile keyword.

/*** * DCL mode *@author Iflytek_dsw
 *
 */
public class Singleton {
	private volatile static Singleton instance;
	private Singleton(a){};
	
	public static Singleton getInstance(a){
		if(instance == null) {synchronized (Singleton.class) {
				if(instance == null){
					instance = newSingleton(); }}}returninstance; }}Copy the code

The highlight of the implementation of the double-checked locking pattern is the implementation of the getInstance method and the definition of the instance. It does this by determining null values twice. The first judgment is mainly for unnecessary locking mechanisms, and the second judgment is to ensure that instances are created under NULL conditions. The use of the volatile keyword to define instance ensures consistency. DCL mode has high resource utilization, and only the first time getInstance is called does the initialization continue.

Static inner class

/*** * Static inner class schema *@author Iflytek_dsw
 *
 */
public class Singleton {
	private Singleton(a){};
	
	public static synchronized Singleton getInstance(a){
		return SingleBuilder.instance;
	}
	
	private static class SingleBuilder{
		private static final Singleton instance = newSingleton(); }}Copy the code

This approach also takes advantage of the classloder mechanism to ensure that instance is initialized with only one thread. Even if the Singleton class is loaded, the instance does not have to be initialized because the SingleBuilder class is not actively used. The SingleBuilder class is loaded to instantiate instance. On the other hand, I don’t want to instantiate the Singleton class when it loads because I can’t be sure that the Singleton class might be used actively somewhere else to get loaded. Instantiating instance is obviously not appropriate at this point and is highly recommended.

Enumeration singletons have one and only one copy of each instance of an enumeration in the Java heap, which makes it easy to implement the singleton pattern.

enum SingletonEnum{
	INSTANCE;
	
	
	public void drawCircle(a){
		System.out.print("drawCircle"); }}public static void main(String[] args) {
		SingletonEnum.INSTANCE.drawCircle();
	}
Copy the code

This approach, advocated by Effective Java author Josh Bloch, not only avoids multithreaded synchronization problems, but also prevents deserialization from recreating new objects, and is highly recommended. Enumerations in Java can have their own fields and methods just like normal classes, and most importantly, the creation of instances of enumerations is thread-safe, a singleton in any case.

Record singletons are guaranteed by customizing the manager and then recording the creation of the management instance.

/*** * record singleton *@author Iflytek_dsw
 *
 */
public class Singleton {
	private static Map<String,Object> keyMap = new HashMap<>();
	private Singleton(a){};
	
	public static Singleton getInstance(String key){
		return (Singleton) keyMap.get(key);
	}
	
	public static void registerSingle(String clazzName,Object obj){
		if(keyMap.get(clazzName) == null){ keyMap.put(clazzName, obj); }}}Copy the code

A recording singleton actually maintains a set of instances of a singleton class in a Map (registry) that is returned directly from the Map for registered instances, and is registered and returned for unregistered instances.

conclusion

Either way the singleton pattern is implemented, the core is the private constructor and the single instance is obtained through a static method, and the variation in this process is to ensure that the thread is safe and the placement serialization leads to the problem of newly generated objects. The choice depends on the environment: whether it’s multithreaded, performance requirements, JDK version, etc.