当前位置:网站首页>Implementation of singleton mode and prevention of reflection and serialization

Implementation of singleton mode and prevention of reflection and serialization

2022-07-24 00:48:00 Handsome yuppie

The singleton pattern

Pattern motivation

For some classes in the system , Only one example is important , For example, there is only one timing tool and ID( Serial number ) generator .

The application of singleton mode includes : The system only needs one instance object ; Only one common access point is allowed for a single instance of a client call class .

Definition

seeing the name of a thing one thinks of its function , Used to ensure that only one instance of an object can be created , besides , It also provides global access to instances .

There are three main points of the singleton model : First, a class can only have one instance ; Second, it must create this instance by itself ; Third, it must provide the whole system with this instance by itself .

Realization

To ensure the uniqueness of the singleton instance , be-all Singleton constructors are declared private Of , Re adoption statement Static method to achieve global access The singleton instance .

Slacker type
public class Singleton {
    
  private static Singleton instance;
  private Singleton(){
    }

  public static Singleton getInstance(){
    
    if(instance == null)
      instance = new Singleton();
    return instance;
  }
    
  public void doAction(){
    
    //TODO  Achieve what you need to do 
  }
}

Slacker type , As the name suggests, an instance is created only when it is used ,“ Compare the lazy ”, Check whether there are instances when using , Return if any , If not, build a new one . There are two ways to write thread safe and thread unsafe , The difference is synchronized keyword .

Hungry Chinese style
public class Singleton {
    
  private static Singleton instance = new Singleton();
  private Singleton(){
    }

  public static Singleton getInstance(){
    
    return instance;
  }
    
  public void doAction(){
    
    //TODO  Achieve what you need to do 
  }
}

Hungry Chinese style , It's also easy to understand from the name , Namely “ Relatively diligent ”, Examples are in initialization It's already built by the time of , Whether you use it or not , It's all built first . The advantage is No thread safety issues ( The class loading mechanism is used to avoid the problem of multithreading synchronization ), The downside is a waste of memory space .

Double check lock (Double Check)( recommend )
public class Singleton {
    
  private volatile static Singleton instance = null;
  private Singleton(){
    }

  public static Singleton getInstance(){
    
    if(instance == null)
      synchronized (Singleton.class){
    
        if(instance == null)
          instance = new Singleton();
      }
    return instance;
  }
    
  public void doAction(){
    
    //TODO  Achieve what you need to do 
  }
}

advantage :

  1. Thread safety
  2. Realization Lazy loading
  3. More efficient
principle :

1、 first if Verification is for Improve code execution efficiency , Because the singleton mode only needs to create an instance once , So when the instance is created , Call again getInstance Method does not have to compete for the synchronized code block , Simply return to the previously created instance .

2、 the second if Verification is for Prevent secondary instance creation . Because for the first time if Verification is not synchronized , It is possible that multiple threads have entered the first if There are competing resources , If there is no second verification , t1,t2 Are competing for synchronization resources ,t2 After getting the resources , Create examples , Then the resources are released ,t1 Get resources , t1 An instance will also be created , that , Multiple instances will be created , therefore , The second time if Check OK Completely avoid the problem of creating multiple instances caused by multithreading .

private volatile static Singleton instance = null; there volatile essential ,volatile Keywords can avoid JVM Instruction rearrangement optimization .

because instance = new Singleton(); It can be divided into three steps :

  1. by singleton Allocate memory space ;
  2. initialization singleton;
  3. take singleton Point to allocated memory space ;

But because of JVM It has the property of instruction reordering , The execution order may change to 1-3-2. Instruction reordering does not cause problems in a single thread , But in Multithreading will cause the thread to obtain an uninitialized instance . for example : Threads T1 Yes 1 and 3, here T2 call getInstance() After the discovery singleton Not empty , Therefore return singleton, But at this time singleton It's not initialized yet .
Use volatile It will be banned JVM Command rearrangement , So as to ensure the normal execution under multithreading .

Static inner class ( recommend )
public class Singleton {
    
    private static class SingletonHolder {
    
        private static Singleton instance = new Singleton();
    }
    private Singleton() {
    
        
    }
    public static Singleton getInstance() {
    
        return SingletonHolder.instance;
    }
    
    public void doAction(){
    
    	//TODO  Achieve what you need to do 
  	}
}

This method uses the class loading mechanism to ensure that there is only one thread when initializing the instance , Static inner class in Singleton When loaded, it is not instanced immediately , It's calling getInstance() Static inner classes will be loaded only when , To complete the Singleton Instantiation . Because the static properties of the class will only be initialized when the class is loaded for the first time , Just through JVM The thread safety feature when loading classes ensures thread safety . There may also be reflection attacks or deserialization attacks .

advantage :

  1. utilize JVM The mechanism of loading static internal classes ensures multithreading safety
  2. Realization Lazy loading effect
  3. Efficient
Enumeration implementation ( recommend )
public enum Singleton {
    
    INSTANCE;
    public void doSomething() {
    
        System.out.println("doSomething");
    }
}

Automatic support for serialization mechanism , Absolutely prevent multiple instantiations .

advantage :

  1. Thread safety ( The creation of enumeration instances is thread safe by default )
  2. Prevent reflection , Destruction of singleton by cloning and serialization

Three ways of ring breaking single case mode : Reflection , serialize , clone

The implementation of singleton mode and how to effectively prevent reflection and deserialization - Call me Lord Peng - Blog Garden (cnblogs.com)

With Double Check As an example , Test reflection , serialize , Whether cloning can break the ring singleton mode :

public class Singleton implements Serializable,Cloneable {
    
    private static volatile Singleton singleton;
    
    private Singleton(){
    }
    
    public static Singleton getInstance(){
    
        if(singleton == null){
    
            synchronized (Singleton.class){
    
                if(singleton == null){
    
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
    
        return super.clone();
    }
}

The test case :

public static void main(String[] args) throws Exception {
    
        Singleton instance = Singleton.getInstance();
        System.out.println(" The original  singleton  Of  hashcode: " + instance.hashCode());
        // Reflection 
        Constructor<Singleton> declaredConstructor = Singleton.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Singleton singleton = declaredConstructor.newInstance();
        System.out.println(" Reflection gets  singleton  Of  hashcode: " + singleton.hashCode());
        
        // clone 
        Singleton clone = (Singleton) Singleton.getInstance().clone();
        System.out.println(" Clone obtained  singleton  Of  hashcode: " + clone.hashCode());

        // serialize 
        ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
    	ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(Singleton.getInstance());
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        Singleton serialize = (Singleton) ois.readObject();
        // Close the resource 
        System.out.println(" Serialize the obtained  singleton  Of  hashCode: "+ serialize.hashCode());
    }

Output results :

 The original  singleton  Of  hashcode: 460141958
 Reflection gets  singleton  Of  hashcode: 1163157884
 Clone obtained  singleton  Of  hashcode: 1956725890
 Serialize the obtained  singleton  Of  hashCode: 666641942

The operation results show that getInstance()、 Reflection 、 clone 、 Serialize these four ways to get Singleton Object's hashCode It's different , At this time, the singleton mode has been broken

How to prevent reflection 、 clone 、 Serialize the broken loop of the singleton pattern

1、 Prevent reflection from breaking the ring ( Although the construction method has been privatized , But using the reflection mechanism newInstance() Method constructors can also be called ):

  • First define a global variable switch isFristCreate On by default
  • Change its state to off state when it is loaded for the first time

2、 Prevent cloning from breaking the ring

  • rewrite clone(), Returns the singleton object directly

3、 Prevent serialization from breaking the ring

  • add to readResolve(), return Object object
public class Singleton implements Serializable, Cloneable {
    
    private static final long serialVersionUID = 6125990676610180062L;
    private volatile static Singleton singleton;
    private static boolean isFristCreate = true;// The default is to create for the first time 

    private Singleton() {
    
        if (isFristCreate) {
    
            synchronized (Singleton.class) {
    
                if (isFristCreate) {
    
                    isFristCreate = false;
                }
            }
        } else {
    
            throw new RuntimeException(" Has been instantiated once , Can no longer instantiate ");
        }
    }

    public void doAction() {
    
        //TODO  Achieve what you need to do 
    }

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

    @Override
    protected Singleton clone() throws CloneNotSupportedException {
    
        return singleton;
    }

    private Object readResolve() {
    
        return singleton;
    }
}

The test case :

public static void main(String[] args) throws Exception {
    
        Singleton instance = Singleton.getInstance();
        System.out.println(" The original  singleton  Of  hashcode: " + instance.hashCode());
        
        // clone 
        Singleton clone = (Singleton) Singleton.getInstance().clone();
        System.out.println(" Clone obtained  singleton  Of  hashcode: " + clone.hashCode());

        // serialize 
        ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(Singleton.getInstance());
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        Singleton serialize = (Singleton) ois.readObject();
        // Close the resource 
        System.out.println(" Serialize the obtained  singleton  Of  hashCode: "+ serialize.hashCode());
        
        // Reflection 
        Constructor<Singleton> declaredConstructor = Singleton.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Singleton singleton = declaredConstructor.newInstance();
        System.out.println(" Reflection gets  singleton  Of  hashcode: " + singleton.hashCode());
    }

test result :

 The original  singleton  Of  hashcode: 460141958
 Clone obtained  singleton  Of  hashcode: 460141958
 Serialize the obtained  singleton  Of  hashCode: 460141958
Exception in thread "main" java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at pc.TestSingleton.main(TestSingleton.java:37)
Caused by: java.lang.RuntimeException:  Has been instantiated once , Can no longer instantiate 
	at pc.Singleton.<init>(Singleton.java:24)
	... 5 more

Related articles

[1] Five ways to write singleton mode _absolute_chen The blog of -CSDN Blog _ The singleton pattern

[2] Design patterns ( Two ) Seven ways to write singletons _Android Advanced trilogy - Liu Wangshu -CSDN Blog _ Single mode writing

[3] The implementation of singleton mode and how to effectively prevent reflection and deserialization - Call me Lord Peng - Blog Garden (cnblogs.com) ( recommend )

原网站

版权声明
本文为[Handsome yuppie]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/204/202207230656481306.html