This is the second day of my participation in Gwen Challenge

The singleton pattern

The singleton pattern is one of the most frequently noticed and used design patterns.

  • Features: The singleton mode ensures that only one instance exists globally in the system, and other objects perform relative work through it, thus reducing the resource consumption of repeatedly creating instances.

implementation

Lazy mode

Slacker is the most basic and simplest implementation in the singleton pattern. The CPU object is instantiated when the class is initialized. This can increase resource expenditure to some extent, but the disadvantage is that it consumes resources before formal use and is not thread-safe. In addition, the purpose of adding synchronized is to ensure the uniqueness of singleton objects (not 100% uniqueness) in multi-threaded environment, but synchronization overhead is also brought by the need for simultaneous multi-threading. So the slacker pattern is not the optimal singleton pattern.

public class CPU{
    private static CPU instance;
    private CPU(a){}
    public static synchronized CPU getInstance(a){
        if(instance == null){
            instance = new CPU();
        }
        returninstance; }}Copy the code

Double CheckLock (DCL)

The DCL is an enhanced version of lazy mode, with enhanced thread-safety protection and no thread synchronization lock operations after initialization. DCL places synchronized inside getInstance only when instance is initialized, and when instance is instantiated and not empty, thread synchronization is not required.

public class CPU{
    private static int count = 0; // Add a count defense reflection call
    private static CPU instance = null;
    private CPU(a){}
    public static CPU getInstance(a){
        if(instance == null) {synchronized(CPU.class){
               instance = new CPU();
               if(count > 0) {throw new RuntimeException("Two instances created");
               }
               count++;
        }
        returninstance; }}Copy the code

However, in the Java compiler, DCL can still have timing problems. Instance = new CPU() is not an atomic operation, and there is a risk that instance will return an incomplete object in multiple threads. Therefore, the solution to this problem is to use volatile to sacrifice a bit of performance so that each instance can be read from main memory to ensure uniqueness.

The DCL code optimized again looks like this

public class CPU{
    private volatile static CPU instance = null;
    private CPU(a){}
    public static CPU getInstance(a){
        if(instance == null) {synchronized(CPU.class){
               instance = newCPU(); }}returninstance; }}Copy the code

Static inner class

The DCL approach is actually not perfect. The static inner class pattern ensures that sInstance is not initialized when getInstance is not called. The inner class SingletonHolder instantiates sInstance only when getInstance is called. At the same time, static inner classes are thread-safe because the virtual machine takes special care of the client of the class. When multiple threads are deinitialized at the same time, only one thread will execute and the other threads will block until the initialization is complete. This ensures that static inner classes are thread-safe, ensuring uniqueness and delaying instantiation of objects. Static inner classes are not perfect, however, and do not support initial input arguments.

public class CPU{
    private CPU(a){}
    public static CPU getInstance(a){
        return SingletonHolder.sInstance;
    }
    private static class SingletonHolder{
        private static final CPU sInstance = newCPU(); }}Copy the code

The enumeration

Enumerating singletons is the easiest way to implement singletons, but it is not a particularly common pattern. Enumeration features The JVM guarantees thread safety and uniqueness. Another unique feature of enumerations is that they are serializable. Enumerations that are deserialized remain the same instance object, and no new instance object will be created for serialization.

public enum CPU {

    INSTANCE;

    public void doSomething(a) {
        System.out.println("doSomething"); }}Copy the code

conclusion

The singleton pattern comes in many forms, and the core principle is to privatize the constructor and obtain a unique instance through a static method. This process requires instance uniqueness, thread safety, and preventing the sequence table from recreating instances. The choice of singleton in a real business scenario depends on the developer’s judgment of the development environment: high concurrency, resource consumption, locale versions, etc.