Design pattern - singleton pattern

Posted by monloi on Tue, 01 Feb 2022 18:44:23 +0100

Design pattern - singleton pattern

concept

Basic introduction
The so-called singleton design pattern is to take certain methods to ensure that there is only one object instance for a class in the whole software system, and the class only provides a method to obtain its object instance (static method);

Single case mode of eight writing methods

  • Hungry Han formula (static constant)
  • Hungry Chinese style (static code block)
  • Lazy (thread unsafe)
  • Lazy (thread safety, synchronization method)
  • Lazy style (thread safety, improve the fourth way)
  • duplication check
  • Static inner class
  • enumeration

Details and precautions

  • The singleton mode ensures that there is only one object in the system memory, saving system resources. For some objects that need to be created and destroyed frequently, using the singleton mode can improve the system performance;
  • When you want to instantiate a singleton class, you must remember to use the corresponding method to obtain the object instead of new;
  • Scenarios for using singleton mode: objects that need to be created and destroyed frequently, objects that take too much time or resources to create objects (i.e. heavyweight objects), but are often used, tool objects, objects that frequently access databases or files (such as data sources, session factories, etc.);

Hungry Han formula (static constant)

That is, create a static member directly in the class, and then make the static member available to the outside through a method, as shown in the following code;

public class Type1Test {

	public static void main(String[] args) {
		Singleton instance1 = Singleton.getInstance();
		Singleton instance2 = Singleton.getInstance();
		
		System.out.println("instance1 of hashCode = " + instance1.hashCode() + "; instance2 of hashCode = " + instance2.hashCode());
		System.out.println("instance1 and instance2 Is it the same object" + (instance1 == instance2));
		
	}
}

class Singleton {
	// 1. Privatization constructor
	private Singleton() {}
	
	// 2. Class
	private static final Singleton instance = new Singleton();
	
	// 3. Provide a static public method to get the instance object
	public static Singleton getInstance() {
		return instance;
	}
	
}

Analysis of advantages and disadvantages:

  • This method is simple to write, that is to complete the instantiation when the class is loaded, which avoids the problem of thread synchronization and is thread safe;
  • Instantiation is completed when the class is loaded, which does not achieve the effect of lazy loading. If this instance is not used from beginning to end, it will cause a waste of memory;
  • This method is based on the classLoader mechanism and avoids the synchronization problem of multiple threads. However, instance is instantiated during class loading. In the singleton mode, most of them call getInstance() method. At this time, there are many reasons for class loading. Therefore, it is uncertain that there are other ways (or other static methods) to cause class loading, At this time, initializing instance does not achieve the effect of lazy loading;
  • The singleton mode implemented in this way can be used, but it may cause a waste of memory;

Hungry Chinese style (static code block)

Executing the above object instantiation process in a static code block;

public class Type2Test {
	public static void main(String[] args) {
		Singleton instance1 = Singleton.getInstance();
		Singleton instance2 = Singleton.getInstance();
		
		System.out.println("instance1 of hashCode = " + instance1.hashCode() + "; instance2 of hashCode = " + instance2.hashCode());
		System.out.println("instance1 and instance2 Is it the same object" + (instance1 == instance2));
		
	}
}

class Singleton {
	
	// 1. Privatization constructor
	private Singleton() {}
	
	private static Singleton instance;
	// 2. Static code block
	static {
		instance = new Singleton();
	}
	
	// 3. Provide a static public method to get the instance object
	public static Singleton getInstance() {
		return instance;
	}
	
}

Analysis of advantages and disadvantages:

  • The method is as like as two peas, but only the process of instantiation is placed in the static code block, and the code in the static block is initialized when class loading is loaded, and the instance of the class is initialized. The advantages and disadvantages are exactly the same as the way.
  • The singleton mode implemented in this way can be used, but it may cause a waste of memory;

Lazy (thread unsafe)

public class Type3Test {
	public static void main(String[] args) {
		Singleton instance1 = Singleton.getInstance();
		Singleton instance2 = Singleton.getInstance();
		
		System.out.println("instance1 of hashCode = " + instance1.hashCode() + "; instance2 of hashCode = " + instance2.hashCode());
		System.out.println("instance1 and instance2 Is the same object:" + (instance1 == instance2));
		
	}
}

class Singleton {
	
	private static Singleton instance;
	
	// 1. Privatization constructor
	private Singleton() {}
	
	// 2. Provide a static public method to get the instance object
	public static Singleton getInstance() {
		if(instance == null) {
			instance = new Singleton();
		}
		return instance;
	}

}

Analysis of advantages and disadvantages:

  • It has the effect of lazy loading, but it can only be used under single thread (thread is not safe);
  • If one thread enters the if(instance == null) judgment statement in multithreading, it may not have time to execute it, and another thread also passes the judgment statement. At this time, multiple instances will be generated, so this method cannot be used in multithreading environment;
  • In the actual development, do not use this method to realize the singleton mode;

Lazy (thread safety, synchronization method)

public class Type4Test {
	public static void main(String[] args) {
		Singleton instance1 = Singleton.getInstance();
		Singleton instance2 = Singleton.getInstance();
		
		System.out.println("instance1 of hashCode = " + instance1.hashCode() + "; instance2 of hashCode = " + instance2.hashCode());
		System.out.println("instance1 and instance2 Is it the same object" + (instance1 == instance2));
		
	}
}

class Singleton {
	
	private static Singleton instance;
	
	// 1. Privatization constructor
	private Singleton() {}
	
	// 2. Provide a static public method to get the instance object
	public static synchronized Singleton getInstance() {
		if(instance == null) {
			instance = new Singleton();
		}
		return instance;
	}
}

Analysis of advantages and disadvantages:

  • The thread unsafe problem is solved;
  • The efficiency is too low. When each thread wants to obtain an instance of a class, it needs to synchronize the execution of getInstance() method. In fact, this method only needs to execute the instantiation code once. For later users who want to obtain an instance of this class, just return directly. However, when using this method, all the method bodies are synchronized, which is too inefficient;
  • In the actual development, this method is not recommended to implement the singleton mode;

Lazy style (thread safety, improve the fourth way), note that the improvement of this method fails

public class Type5Test {
	public static void main(String[] args) {
		Singleton instance1 = Singleton.getInstance();
		Singleton instance2 = Singleton.getInstance();
		
		System.out.println("instance1 of hashCode = " + instance1.hashCode() + "; instance2 of hashCode = " + instance2.hashCode());
		System.out.println("instance1 and instance2 Is it the same object" + (instance1 == instance2));
		
	}
}

class Singleton {
	
	private static Singleton instance;
	
	// 1. Privatization constructor
	private Singleton() {}
	
	// 2. Provide a static public method to get the instance object
	public static Singleton getInstance() {
		if(instance == null) {
			synchronized(Singleton.class) {
				instance = new Singleton();
			}
		}
		return instance;
	}
	
}

Analysis of advantages and disadvantages:

  • This method is intended to improve the fourth method, because the efficiency of the former is too low;
  • However, careful analysis shows that this method can not achieve the effect of synchronization, because when multiple threads enter if(instance == null), synchronized() {} can only let these threads create instance instances one by one, and multiple instance instances will be generated;
  • In the actual development, this method cannot be used to realize the singleton mode;

Double check mode

public class Type6Test {
	public static void main(String[] args) {
		Singleton instance1 = Singleton.getInstance();
		Singleton instance2 = Singleton.getInstance();
		
		System.out.println("instance1 of hashCode = " + instance1.hashCode() + "; instance2 of hashCode = " + instance2.hashCode());
		System.out.println("instance1 and instance2 Is it the same object" + (instance1 == instance2));
		
	}
}

class Singleton {
	
	private static volatile Singleton instance; // volatile modifies this variable to mean that when this variable changes, the value will be immediately brushed into main memory to achieve the effect of synchronization to a certain extent
	
	// 1. Privatization constructor
	private Singleton() {}
	
	// 2. Provide a static public method to get the instance object
	public static Singleton getInstance() {
		if(instance == null) {
			synchronized(Singleton.class) {
				if(instance == null) {
					instance = new Singleton();
				}
			}
		}
		return instance;
	}
	
}

Analysis of advantages and disadvantages:

  • Double check is often used in multithreading development; As shown in the following code, two if(instance == null) checks are used to ensure that the thread is safer. In this way, the instantiated code is executed only once. When this method is executed for the first time, even if the external if(instance == null) enters multiple threads, there will be an if(instance == null) inside to ensure that only one instance can appear and multiple instances will not be created; When you visit again later, you will directly return instance after passing the if(instance == null) of the outer layer, which avoids the execution of synchronized code block (singleton. Class) and greatly improves the inspection efficiency;
  • The singleton mode implemented in this way is thread safe, can delay loading and has high efficiency;
  • In the actual development, it is recommended to use this method to realize the singleton mode;

Static internal class mode

public class Type7Test {
	public static void main(String[] args) {
		Singleton instance1 = Singleton.getInstance();
		Singleton instance2 = Singleton.getInstance();
		
		System.out.println("instance1 of hashCode = " + instance1.hashCode() + "; instance2 of hashCode = " + instance2.hashCode());
		System.out.println("instance1 and instance2 Is it the same object" + (instance1 == instance2));
		
	}
}

/*
 * Notice:
 * 	Features of static internal classes:
 * 		1,When the main class is loaded, the static inner class will not be loaded;
 * 		2,When the main class calls the static variables of the static inner class, the static inner class will be loaded;
 * 
 */
class Singleton {
	
	// 1. Privatization constructor
	private Singleton() {}
	
	// 2. Create a static inner class and instantiate an instance of Singleton inside it
	private static class SingletonInstance {
		private static final Singleton INSTANCE = new Singleton();
	}
	
	// 3. Provide a static public method to get the instance object
	public static Singleton getInstance() {
		return SingletonInstance.INSTANCE;
	}
	
}

Analysis of advantages and disadvantages:

  • This method adopts the mechanism of class loading to ensure that there is only one thread when initializing the instance (thread safety);
  • In the following code, the static internal class method will not be instantiated immediately when the Singleton class is loaded, but when instantiation is required, calling the getInstance() method will lead to the loading of the Singleton class, so as to complete the instantiation of Singleton;
  • The static properties of the class will only be initialized when the class is loaded for the first time. Therefore, the JVM helps us ensure the safety of threads. Other threads cannot enter when the class is initialized;
  • It avoids thread insecurity and uses the characteristics of static internal classes to realize delayed loading with high efficiency;
  • In the actual development, it is recommended to use this method to realize the singleton mode;

Enumeration mode

public class Type8Test {
	public static void main(String[] args) {
		Singleton instance1 = Singleton.INSTANCE;
		Singleton instance2 = Singleton.INSTANCE;
		
		System.out.println("instance1 of hashCode = " + instance1.hashCode() + "; instance2 of hashCode = " + instance2.hashCode());
		System.out.println("instance1 and instance2 Is it the same object" + (instance1 == instance2));
		
		instance1.method1();
		instance2.method1();
	}
}

enum Singleton {
	INSTANCE;
	public void method1() {
		System.out.println("method method1 Yes...");
	}
}

Analysis of advantages and disadvantages:

  • With jdk1 5, which can not only avoid the problem of multi-threaded synchronization, but also prevent deserialization and re creation of new objects;
  • In the actual development, it is recommended to use this method to realize the singleton mode;

Topics: Java Design Pattern