This is the fifth day of my participation in the August More text Challenge. For details, see:August is more challenging

The singleton pattern

  • The singleton pattern is a creation pattern in which a singleton class is responsible for creating its own objects and has only one instance object per class, and provides that instance to the entire system. The system can access this instance directly without needing to instantiate it
  • Characteristics of the singleton pattern:
    • A singleton class has only one instance
    • A singleton class must create its own unique instance
    • A singleton class must provide a unique instance created for the rest of the system object

The implementation of the singleton pattern

  • Singleton pattern to ensure a class has only one instance, and provide a global access, is mainly used to solve a global use of the class frequently created and destroyed, by judging system exists the singleton to solve this problem, if you have the singleton returns the singleton, otherwise, they create the singleton, just make sure the constructor is private
    • Ensure that there is only one instance of a class: just make the constructor of the class private
    • Provide a global access point to that instance: the singleton class creates its own instance, providing a static method as the access point to the instance
  • The hungry and the lazy:
    • Slob: Singleton object instances are lazily loaded. They are not created in advance and are created only when the object instance is used
    • Instantiation creates an object instance when a singleton object instance is referenced by a declaration
  • The singleton pattern, which eliminates thread-unsafe slackers, is implemented in five ways:
    • lazy
    • Double check the lock
    • The hungry
    • Static inner class
    • The enumeration
  • In general, the singleton pattern can be realized directly by using hanhan
  • If lazy loading is explicitly required, the singleton pattern is usually implemented using static inner classes
  • If there is an object created about deserialization, consider using enumerations to implement the singleton pattern
  • Static class Static:
    • Static classes are initialized directly the first time they are run and do not need to be used in lazy loading
    • The static class approach is more convenient when there is no need to maintain any state and is only used for global access
    • Singletons are appropriate for situations where inheritance is needed or some particular state needs to be maintained

Thread unsafe lazy

  • Singleton mode thread unsafe lazy Singleton example
  • Lazy load mode is used
  • There is the problem of creating multiple instances when multiple threads call the getInstance() method in parallel. That is, it doesn’t work in multithreaded mode

Thread-safe slob

  • The Singleton pattern is thread-safe for the lazy Singleton example
  • Solves the problem of creating multiple instances in multi-threaded environment
  • The problem is that you need to apply for a lock every time you acquire an instance, and the method is inefficient because only one thread can call the getInstance() method at any one time

Double check the lock

  • Double-checked lock mode: doule checked locking pattern
    • Use synchronous block locking methods
    • There will be two testsinstance == null
      • Once outside of the synchronized block
      • Once in the synchronization block
        • Because there will be multiple threads going into the if outside of the synchronized block
        • Failure to perform a second check in a synchronized block can result in multiple instances being generated
  • Example of double-locking Singleton mode
  • volatile:
    • For instructions in the computer,CPU and compiler usually optimize instructions according to certain rules in order to improve the execution efficiency of the program
    • If two instructions are not dependent on each other, then the instructions may not be executed in the order in which the source code was written
    • likeinstance = new Instance()Method to create an instance execution is divided into three steps:
      • Allocate object memory space: Allocates memory to the newly created Instance object
      • Initialize the object: Calls the constructor of the singleton class to initialize the member variables
      • Set instance to point to the memory address allocated to the newly created object. = null
        • Because of the above initialization objects and SettingsinstanceThe memory address allocated to the newly created object does not have a data dependency, and whichever step is executed first will not affect the final result, so the order of the program will change when it is compiled:
          • Allocate object memory space
          • Set instance to point to the memory address allocated to the newly created object
          • Initialize an object
        • CPUWhen reordering instructions, the compiler does not care whether the reordering affects the execution results of multiple threads. If you don’t addvolatileKeyword if there are multiple threads accessinggetInstance()Method, if an instruction reorder happens to occur, the following may happen:
          • When the first thread obtains the lock and enters the second if method, it allocates the memory space first, and then instance points to the address it just allocated. In this case, instance is not null. However, the instance has not been initialized yet
          • If getInstance() is called by another thread and the result of the first if is false, it will return an uninitialized instance, which may cause an NPE exception
    • The reason for using volatile is to disallow reordering of instructions:
      • There is a memory isolation after a volatile variable is assigned
      • Read operations are not reordered into memory isolation
      • For example, in the above operation, the read operation must be performed after 1,2,3 or 1,3,2, otherwise the related results will not be read

The hungry

  • The Singleton pattern feeds the Singleton example
  • Advantages:
    • In a singleton class, an object instance is created when the class is loaded. Because instances of singletons are declared static final variables and are initialized the first time the class is added to memory, creating the instances themselves is thread-safe
  • Disadvantages:
    • Hangry mode is not a lazy loading mode. The singleton class is initialized the first time the class is loaded, even if the client does not call the getInstance() method
    • Creating an instance of a singleton class using hunk mode does not work in some scenarios:
      • For example, because the instance created by Hunham is declared as final
      • If the creation of an instance of the Singleton class depends on parameters or configuration files
      • You need to call the method to set parameters for the instance of the singleton class before the getInstance() method, in which case this hungry mode is not available

Static inner class

  • Example of the static inner class Singleton
  • Using the static inner class pattern to create a singleton class instance is usedJVMThe mechanism ensures thread safety:
    • Static singleton objects are not directly instantiated as member variables of the singleton class, so the singleton class is not instantiated when the class loads
    • The static inner class Nest is loaded the first time the getInstance() method is called. If a variable of type instance is defined in a static inner class, the variable is initialized first
    • Ensure thread safety through the JVM by ensuring that the member variable is initialized only once
    • Since the getInstance() method does not have a thread lock, there is no performance impact
  • Advantages of static inner classes:
    • The static inner class Nest is private and can only be accessed through the getInstance() method, so it is lazily loaded
    • The synchronization lock is not acquired when the instance is read, and the performance is good
    • Static inner classes do not depend on the JDK version

The enumeration

  • The Singleton pattern enumerates the Singleton example
  • The best thing about implementing singletons using enumerations is that they are very simple
  • The INSTANCE can be accessed through eno.instance, which is simpler than the getInstance() method
  • The creation of enumerations by default is thread-safe and prevents reflection and deserialization from causing new objects to be created
    • Enum The Enum type is used within the class to prevent the creation of new objects through reflection
    • The Enum class serializes the object by its type and enumeration name, and then finds the unique instance of the object in memory by matching the enumeration name with the valueOf() method, preventing the creation of new objects during deserialization
  • Slackish and hungrier implementations of singletons: Whether implemented through slackish or hungrier implementations of singletons, it is possible to break the singletons’ properties through reflection and deserialization, creating multiple objects
  • Reflection breaks the singleton pattern: With reflection, you can force access to the private constructor of a singleton class to create new objects
public static void main(String[] args) {
	// Get the constructor of the singleton class using reflection
	Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
	// Set access to the private constructor
 	constructor.setAccessiable(true);
 	// Use reflection to create new objects
 	Singleton newInstance = constructor.newInstance();
 	// Create a singleton object using the singleton pattern
 	Singleton singletonInstance = Singleton.getInstance();
 	// If the two objects are different, return false
 	System.out.println(singletonInstance  == newInstance);
}
Copy the code
  • Deserialization breaks the singleton pattern: reading an object through the readObject() method returns a new object instance
public static void main(String[] args) {
	// Create an output stream object
	ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("Singleton.file"));
	// Write the singleton to the file
	Singleton singletonInstance = Singleton.getInstance();
	os.writeObject(singleton);
	// Read the singleton from the file
	File file = new File("Singleton.file");
	ObjectInputStream is = new ObjectInputStream(new FileInputStream(file));
	Singleton newInstance = (Singleton)is.readObject();
	// If the two objects are different, return false
	System.out.println(singletonInstance == newInstance);
}
Copy the code