Enumeration is best for singleton mode

Posted by churdaddy on Tue, 25 Jun 2019 18:59:09 +0200

When it comes to the singleton model, I think the first thing you think about is the lazy, evil-looking style.As for registration (obsolete mode, negligible).

The singleton pattern has the following characteristics:
1. A singleton class can only have one instance.
2. A singleton class must create its own unique instance.
3. The singleton class must provide this instance to all other objects.

1. Lazy singleton

Write a lazy singleton pattern first.

public class Singleton {  
    private Singleton() {}  
    private static Singleton single=null;  
    public static Singleton getInstance() {  
         if (single == null) {    
             single = new Singleton();  
         }    
        return single;  
    }  
} 

Singleton prevents other classes from being instantiated by accessing the constructor by restricting the construction method to private. Within the same virtual machine scope, the only instance of Singleton can only be accessed by the static getInstance() method.

However, the above code does not take thread security into account, that is, the instance is thread safe.In the case of concurrency, it is possible that when a thread first enters the getInstance() method at the time of instantiation creation, that is, before it is created successfully, b thread also enters the getInstance() method, when a thread instance has not been completed successfully, b thread judges that single is empty and begins to create instances, resulting in two instances being created.

There are three solutions:

public static synchronized Singleton getInstance() {  
    if (single == null) {    
        single = new Singleton();  
    }    
    return single;  
}  

With the synchronized keyword, concurrency can only be accessed by one queue for the getInstance() method.

    public static Singleton getInstance() {  
        if (singleton == null) {    
            synchronized (Singleton.class) {    
               if (singleton == null) {    
                  singleton = new Singleton();   
               }    
            }    
        }    
        return singleton;   
    } 

Double-check locking, which is superior to the above method, does not require queuing into getInstance() to utilize system resources rationally in case of high concurrency and will outperform the above method.

public class Singleton {    
    private static class LazyHolder {    
       private static final Singleton INSTANCE = new Singleton();    
    }    
    private Singleton (){}    
    public static final Singleton getInstance() {    
       return LazyHolder.INSTANCE;    
    }    
} 

The static internal class implements the singleton mode, which is better than the two above methods, which implements thread security without null judgment and outperforms the two above methods.

 

2. Hungry Han Style Single Case

public class Singleton {  
    private Singleton() {}  
    private static final Singleton single = new Singleton();  
    public static Singleton getInstance() {  
        return single;  
    }  
}

Hungry Han is a static loading time, and you don't need to worry about thread security.

 

3. Enumeration singleton mode

The above two methods are singleton modes that are implemented regardless of the radiation and serialization mechanisms, but if radiation is considered, the above singleton class cannot have only one instance.In fact, the Java reflection mechanism allows you to instantiate a class whose construction method is private.This is also the enumeration singleton pattern that we need to introduce now.

public enum  EnumSingleton {
    INSTANCE;
    public EnumSingleton getInstance(){
        return INSTANCE;
    }
}

As an example, you can see that the above example is not very safe. Take the double-retrieved case pattern as an example, I can create a new instance by using radiation:

public static void main(String[] args) throws Exception {
        Singleton s=Singleton.getInstance();
        Singleton sual=Singleton.getInstance();
        Constructor<Singleton> constructor=Singleton.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        Singleton s2=constructor.newInstance();
        System.out.println(s+"\n"+sual+"\n"+s2);
        System.out.println("Normally, is instantiating two instances the same:"+(s==sual));
        System.out.println("Instantiate whether two instances are the same in the case of single-case attack by reflection:"+(s==s2));
    }

The results are:

cn.singleton.Singleton@1641e19d
cn.singleton.Singleton@1641e19d
cn.singleton.Singleton@677323b6
Normally, instantiate whether two instances are the same:
Instantiate whether two instances are the same with the Reflection Attack singleton pattern:false

Thus, the dual retrieval mode is not the most secure and cannot avoid reflective attacks.

 

Let's check the enumeration's singleton pattern

public static void main(String[] args) throws Exception{
        EnumSingleton singleton1=EnumSingleton.INSTANCE;
        EnumSingleton singleton2=EnumSingleton.INSTANCE;
        System.out.println("Normally, is instantiating two instances the same:"+(singleton1==singleton2));
        Constructor<EnumSingleton> constructor= null;
        constructor = EnumSingleton.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        EnumSingleton singleton3= null;
        singleton3 = constructor.newInstance();
        System.out.println(singleton1+"\n"+singleton2+"\n"+singleton3);
        System.out.println("Instantiate whether two instances are the same in the case of single-case attack by reflection:"+(singleton1==singleton3));
    }

The result will be reported to Exception in thread "main" java.lang.NoSuchMethodException.The reason for this exception is that EnumSingleton.class.getDeclaredConstructors() takes all constructors and finds that there is no parameterless constructor we have set, that there is only one parameter (String.class,int.class) constructor, and that when reflection creates an object through newInstance, it checks whether the class is ENUM-modified, and if it throws an exception, the reflection fails.So enumeration is not afraid of launching attacks.

newInstance method source:

public T newInstance(Object ... initargs)
        throws InstantiationException, IllegalAccessException,
               IllegalArgumentException, InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, null, modifiers);
            }
        }
        if ((clazz.getModifiers() & Modifier.ENUM) != 0)
            throw new IllegalArgumentException("Cannot reflectively create enum objects");
        ConstructorAccessor ca = constructorAccessor;   // read volatile
        if (ca == null) {
            ca = acquireConstructorAccessor();
        }
        @SuppressWarnings("unchecked")
        T inst = (T) ca.newInstance(initargs);
        return inst;
    }

Topics: PHP Java