The singleton pattern
1. Singleton schema definition
The singleton pattern is a relatively simple pattern and is defined as follows:
Ensure a class has only one instance, and provide a global point of access to it.
2. Simple implementation of singleton pattern
The emperor class
package cn.edu.njtech;
public class Emperor {
private static final Emperor emperor = new Emperor();
private Emperor(a) {
// Secular and moral constraints, the purpose is not to have a second emperor
}
public static Emperor getInstance(a) {
return emperor;
}
// The Emperor spoke
public static void say(a) {
System.out.println("I am the emperor so-and-so....."); }}Copy the code
Courtiers class
package cn.edu.njtech;
public class Minister {
public static void main(String[] args) {
for (int day = 0; day < 3; day++) {
Emperor emperor = Emperor.getInstance();
emperor.say();
}
// It's an honor to see the same emperor for three days
/* I am the emperor xx..... I am the emperor xx..... I am the emperor xx..... * /}}Copy the code
3. Common code for singleton pattern
package cn.edu.njtech;
public class Singleton {
private static final Singleton singleton = new Singleton();
// Restrict the generation of multiple objects
private Singleton(a) {}// Use this method to get the instance object
public static Singleton getSingleton(a) {
return singleton;
}
// All other methods in the class should be static
public static void doSomeThing(a) {}}Copy the code
4. Application of singleton pattern
Advantages:
- Because the singleton pattern has only one instance in memory, it reduces memory overhead, especially when an object needs to be created and destroyed frequently and performance cannot be optimized during creation and destruction, the singleton pattern has obvious advantages.
- Because the singleton mode only generates one instance, it reduces the performance overhead of the system. When the generation of an object requires more resources, such as reading configuration and generating other dependent objects, it can be solved by directly generating a singleton object when the application is started, and then using the way of permanently resident memory.
- The singleton mode can avoid multiple resource usage, such as a single file write operation. Because only one instance exists in memory, simultaneous write operations on the same resource file are avoided.
- The singleton pattern can set up global access points in the system, optimize and share resource access, for example, you can design a singleton class, responsible for the mapping of all data tables.
Disadvantages:
- The singleton pattern generally has no interface and is difficult to extend. There is basically no other way to extend the singleton pattern except to modify the code. Why can’t singletons implement interfaces? Because the interface makes no sense to the singleton pattern, it requires “self-instantiation” and provides a single instance, the interface or abstract class cannot be instantiated. Of course, in special cases, the singleton pattern can realize the interface, be inherited, etc., need to be judged according to the environment in the system development.
- The singleton pattern is bad for testing. In a parallel development environment, you can’t test until the singleton pattern is complete, and you can’t mock an object without an interface.
- The singleton pattern conflicts with the single responsibility principle. A class should have only one logic, regardless of whether it is singleton or not, depending on the context. The singleton pattern combines “singleton” and business logic in a single class.
Usage scenarios for the singleton pattern:
In a system, a class is required to have only one object. If there are multiple objects, there will be “adverse reactions”. The singleton pattern can be used as follows:
- An environment that requires a unique sequence number to be generated;
- Need a shared access point or shared data throughout the project, such as a Web page counter, can not log every refresh in the database, use a singleton pattern to keep the value of the counter, and ensure that it is thread safe;
- Creating an object consumes many resources, such as accessing IO and database resources.
- For environments that need to define a large number of static constants and static methods (such as utility classes), you can use the singleton pattern (or declare it static)
5. Precautions for singleton mode
First, note the thread-safety issues of the singleton pattern in the case of high concurrency. The singleton pattern can be implemented in several different ways. The above example does not have multiple instances, but the singleton pattern shown in the following code block requires consideration of thread synchronization
package cn.edu.njtech;
public class Singleton {
private static Singleton singleton;
// Restrict the generation of multiple objects
private Singleton(a) {}// Use this method to get the instance object
public static Singleton getSingleton(a) {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
// All other methods in the class should be static
public static void doSomeThing(a) {}}Copy the code
This singleton pattern does not cause problems at low concurrency, but if system stress increases and concurrency increases, multiple instances may appear in memory, disrupting the initial expectation. Why does this happen? Singleton = new singleton (); singleton = new singleton (); So if we keep going, thread A gets an object, thread B gets an object, and we have two objects in memory.
There are many ways to address thread insecurity, including adding the synchronized keyword to the getSingleton method and adding synchronized to the getSingleton method, but these are not good singleton patterns (see appendix). A simple implementation of the singleton pattern is recommended. (Some books refer to this as a hunchman singleton and the above singleton with synchronized as a lazy singleton.)
6. Extension of singleton pattern
If a class can produce multiple objects, the number of objects is not limited, this is very easy to implement, just use the new keyword, if you only need one object, use the singleton pattern is ok, but if you want a class to produce two or three objects? How do you do that? Let’s take the emperor as an example.
Fixed number of emperor classes
package cn.edu.njtech;
import java.util.ArrayList;
import java.util.Random;
public class Emperor {
// Define the maximum number of instances that can be generated
private static int maxNumOfEmperor = 2;
// Each emperor has a name, using an ArrayList to hold the private properties of each object
private static ArrayList<String> nameList = new ArrayList<>();
// Define a list of all instances of the emperor
private static ArrayList<Emperor> emperorList = new ArrayList<>();
// The current emperor serial number
private static int countNumberOfEmperor = 0;
// Generate all objects
static {
for (int i = 0; i < maxNumOfEmperor; i++) {
emperorList.add(new Emperor("The emperor" + (i + 1) + "Emperor")); }}private Emperor(a) {
// Secular and moral constraints do not produce other emperors
}
// Pass in the emperor name to create an emperor object
private Emperor(String name) {
nameList.add(name);
}
public static Emperor getInstance(a) {
Random random = new Random();
// Randomly select an emperor, as long as he is a spiritual leader
countNumberOfEmperor = random.nextInt(maxNumOfEmperor);
return emperorList.get(countNumberOfEmperor);
}
// The Emperor spoke
public static void say(a) { System.out.println(nameList.get(countNumberOfEmperor)); }}Copy the code
The process of courtiers paying homage to the emperor
package cn.edu.njtech;
public class Minister {
public static void main(String[] args) {
// Define 5 ministers
int ministerNum = 5;
for (int i = 0; i < ministerNum; i++) {
Emperor emperor = Emperor.getInstance();
System.out.print("The first" + (i + 1) + "The one who attacked the minister's homage was :"); emperor.say(); }}}Copy the code
Best practices
The singleton pattern is the simplest of the 23 patterns and is widely used. In Spring, for example, every Bean is singleton by default. The advantage of this is that the Spring container can manage the life cycle of these beans, absolutely when they are created, when they are destroyed, what happens when they are destroyed, and so on. If the non-singleton pattern (Prototype type) is used, the management of the Bean after initialization is left to the J2EE container, and the Spring container no longer tracks the Bean life cycle.
The appendix
Thread-safe lazy singleton
package cn.edu.njtech.book;
public class LazySingleton{
// Volatile ensures proper order at the bytecode level
private volatile static LazySingleton instance;
private LazySingleton(a){}Synchronized synchronized locking is equivalent to locking a thread on a method
public static LazySingleton getInstance(a) {
if(instance == null) {synchronized (LazySingleton.class){
if(instance == null){
instance = new LazySingleton();
// Bytecode layer
/* JIT, CPU, 1. Allocate space 2. Reference assignment */}}}returninstance; }}Copy the code