This paper analyzes various implementations of comprehensive singleton pattern in java

  • 2021-09-12 01:24:58
  • OfStack

Directory 1. The idea of singleton pattern 2. N implementation of singleton pattern 2.1, hungry type (thread-safe, available) 2.2, constant type (thread-safe, available) 2.3, lazy type (thread unsafe, concurrent scenario unavailable) 2.4, synchronous lazy type? (thread-safe, available, not recommended) 2.5, double-checked lock DCL (thread-safe, most scenarios meet requirements, recommended) 2.6, static internal class (thread-safe, recommended) 2.7, enumerated singleton (thread-safe, not recommended) 2.8, alternative implementation-using container to implement singleton 2.9, preventing reflection from destroying singleton 2.10, preventing serialization and deserialization from destroying singleton 3. Conclusion

1. The idea of singleton pattern

If you want to sort out some knowledge about java concurrency, you don't know where to start. If you think of thread safety to consider in singleton mode, you should start with singleton mode. Write before singleton pattern, here again summary supplement collation 1, singleton pattern of a variety of implementations.

The main idea of singleton pattern is:

Privatize the construction method (declared as private), so that the outside world can't arbitrarily produce new instance objects in new; Declare a private static instance object for external use; Provide a public method to let the outside world get the instance object of this class

This statement seems true, but it also seems inaccurate. In fact, even if the outside world can produce new instance objects at will, as long as we ensure that the objects we use each time are only 1, we can.

2. N implementation of singleton pattern

2.1. Hungry (thread-safe, available)


public class Singleton {
    private Singleton() {
    }

    private static Singleton sSingleton = new Singleton();

    public static Singleton getInstance() {
        return sSingleton;
    }
}
Disadvantages: Class 1 is instantiated when it is loaded, which occupies system resources in advance.

2.2. Constant (thread-safe, available)


public class Singleton {
    private Singleton() {
    }

    public static final Singleton sSingleton = new Singleton();
}

Use the instance object with the public static final Modified, does not provide an exposed method to get an instance, and directly passes the Singleton.sSingleton Get.

Disadvantages: Like Hungry Type 1, Class 1 is instantiated when it is loaded, which occupies system resources in advance.

2.3. Lazy (thread is unsafe and concurrent scenarios are unavailable)


public class Singleton {
    private Singleton() {
    }

    private static Singleton sSingleton;

    public static Singleton getInstance() {
        if (sSingleton == null) {
            sSingleton = new Singleton();
        }
        return sSingleton;
    }
}
Disadvantages: The first time the first load response is slightly slow, and the thread is unsafe.

2.4. Synchronized lazy style? (Thread-safe, available, not recommended)


public class Singleton {
    private Singleton() {
    }

    private static Singleton sSingleton;

    public synchronized static Singleton getInstance() {
        if (sSingleton == null) {
            sSingleton = new Singleton();
        }
        return sSingleton;
    }
}
Disadvantages: Slow response on the first load, and every time you call getInstance All are synchronized, causing unnecessary synchronization overhead. This mode 1 is generally not recommended.

2.5. Double-checked lock DCL (thread-safe, most scenarios meet the requirements, recommended)


public class Singleton {
    private Singleton() {
    }

    /**
     * volatile is since JDK5
     */
    private static volatile Singleton sSingleton;

    public static Singleton getInstance() {
        if (sSingleton == null) {
            synchronized (Singleton.class) {
                //  Is not initialized, the initial instance Variable 
                if (sSingleton == null) {
                    sSingleton = new Singleton();
                }
            }
        }
        return sSingleton;
    }
}

sSingleton = new Singleton () is not a 1 atomic operation. (XXX) Therefore, it must be added volatile Keyword modification, which is available after jdk 1.5.

Advantages: High resource utilization, the singleton object will be instantiated when getInstance is executed for the first time, and the efficiency is high. Disadvantages: Slow response on the first load and occasional failure due to the Java memory model. In the high concurrency environment, there are also 1-point defects, although the probability of occurrence is very small. DCL pattern is the most used singleton implementation, which can instantiate singleton objects when needed, and can ensure the uniqueness of singleton objects in most scenarios. Unless your code is used in concurrent scenarios that are complex or lower than jdk version 1.6, this method can generally meet the requirements.

2.6. Static inner classes (thread-safe, recommended)


public class Singleton {

    private Singleton () {
    }

    private static class InnerClassSingleton {
       private final static Singleton sSingleton = new Singleton();
    }

    public static Singleton getInstance() {
        return InnerClassSingleton.sSingleton;
    }
}

Advantages: Recommended.

2.7. Enumerate singletons (thread-safe, not recommended)


public enum Singleton{
    INSTANCE;
    
    //  Other methods 
    public void doSomething(){
        ...
    }
}
Advantages: Enumeration implementation singleton is simple and safe. Disadvantages: Experienced Android developers will try to avoid using enumerations. According to the official document, it will cost more than twice as much memory as the static constant Enum.

2.8. Alternative Implementation-Using Containers to Implement Singletons


import java.util.HashMap;
import java.util.Map;

public class Singleton {
    private static Map<String, Object> objMap = new HashMap<String, Object>();

    private Singleton() {
    }

    public static void registerService(String key, Object instance) {
        if (!objMap.containsKey(key)) {
            objMap.put(key, instance);
        }
    }

    public static Object getService(String key) {
        return objMap.get(key);
    }
}

Taking advantage of the non-repeatability of key container HashMap.

Advantages: This implementation enables us to manage various types of singletons, and can obtain operations through the unified 1 interface when using, which reduces the use cost of users, hides the specific implementation for users and reduces the coupling degree. Disadvantages: Without privatization constructor, users can new out new instance objects.

2.9. Prevent reflection from destroying singletons

Many of the previous implementation methods are implemented according to the idea of privatization of construction methods. We know that new objects can still be created by using reflection, so in the reflection scene, the singleton pattern realized by this idea will fail, so how to prevent reflection from destroying the singleton pattern? The principle is to throw an exception when the constructor is called again in the presence of one instance. Let's take the singleton pattern of static inner classes as an example:


public class Singleton {
    private static boolean flag = false;  
  
    private Singleton(){  
        synchronized(Singleton.class)  
        {  
            if(flag == false)  
            {  
                flag = !flag;  
            }  
            else  
            {  
                throw new RuntimeException(" Singleton pattern is violated! ");  
            }  
        }  
    }  

    private static class InnerClassSingleton {
       private final static Singleton sSingleton = new Singleton();
    }

    public static Singleton getInstance() {
        return InnerClassSingleton.sSingleton;
    }
}

2.10. Preventing serialization and deserialization from destroying singletons

By serialization, an object instance can be written to the disk. When it is read back by deserialization, even if the constructor is private, a new instance can still be created by a special way, which is equivalent to calling the constructor of this class. To avoid this problem, we need to add the following method to the code to return an sSingleton object when the readResolve method is executed during deserialization.


private Object readResolve() throws ObjectStreamException {
    return sSingleton;
}

3. Conclusion

Is there a way to implement a singleton pattern that is a singleton in any case?

Yes. Is the enumeration singleton mentioned above. Enumeration can be guaranteed to be singleton and thread-safe in any case.

The above is the analysis of java in a comprehensive singleton pattern of a variety of implementation details, more about java singleton pattern implementation of information please pay attention to other related articles on this site!


Related articles: