Singleton mode expansion explanation-JAVA

Posted Jun 27, 20205 min read

Singleton model expansion

Author: HuiFer
Git-Repo: JavaBook-src

Lazy-style multi-threaded debugging process

  • Write a lazy man

    public class SimpleSingleton {

      public static SimpleSingleton lazy = null;
    
      private SimpleSingleton() {
    
      }
    
      public static SimpleSingleton getInstance() {
          if(lazy == null) {
              lazy = new SimpleSingleton();
          }
    
          return lazy;
      }

    }

  • Create a thread object

    public class ExecutorThread implements Runnable {

      @Override
      public void run() {
          SimpleSingleton instance = SimpleSingleton.getInstance();
          System.out.println("current thread" + Thread.currentThread().getName() + ", current object" + instance);
      }

    }

  • Test class

    public class SimpleSingletonTest {

      public static void main(String[]args) {
          Thread t1 = new Thread(new ExecutorThread());
          Thread t2 = new Thread(new ExecutorThread());
          t1.start();
          t2.start();
      }

    }

  • Breakpoint is given to lazy

Insert picture description here

Switch to thread mode debug

Insert picture description here

A debug window will appear below

Insert picture description here

We execute both threads to the breakpoint

According to the code we can know that as long as there is a sequence, you will get a singleton object. Once you enter at the same time, you may get two objects. Demonstrate through debug

Let the first thread enter the if statement

Insert picture description here

Let the second thread enter

Insert picture description here

Observe the output at this time

End
Current thread Thread-1, current object com.huifer.design.singleton.nw.SimpleSingleton@52fd9092
Current thread Thread-0, current object com.huifer.design.singleton.nw.SimpleSingleton@5a28dc04

Therefore, the above wording is not thread-safe

synchronized

Debug again

The first thread enters

Insert picture description here

The second thread is not allowed to enter

![[External chain image transfer failed, the source site may have an anti-theft chain mechanism, it is recommended to save the image and upload it directly(img-AEhtrtEn-1593263842011)(assets/image-20200627202953735.png)]]( https://i0 . wp.com/segmentfault.com/img/remote/1460000023030857 "[External image transfer failed, the source site may have an anti-theft chain mechanism, it is recommended to save the image and upload it directly(img-AEhtrtEn-1593263842011)(assets/image-20200627202953735 .png)]")

Thread status MONITOR monitoring status

It will become RUNNING only after the first thread is executed

Double verification

public synchronized static SimpleSingleton getInstance01() {
        if(lazy == null) {
            synchronized(SimpleSingleton.class) {
                if(lazy == null) {
                    lazy = new SimpleSingleton();
                }
            }
        }

        return lazy;
    }
  • Also debug

Insert picture description here

![[External chain image transfer failed, the source site may have an anti-theft chain mechanism, it is recommended to save the image and upload it directly(img-UPoZGHhq-1593263842013)(assets/image-20200627203957988.png)]]( https://i0 . wp.com/segmentfault.com/img/remote/1460000023030860 "[External image transfer failed, the source site may have an anti-theft chain mechanism, it is recommended to save the image and upload it directly(img-UPoZGHhq-1593263842013)(assets/image-20200627203957988 .png)]")

Through the two pictures, we can find that the status of Thread-0 is not MONITOR waiting for the completion of Thread-1 execution, switch to Thtread-1 and finish

Insert picture description here

Thread-0 also finished

At this time output results

Current thread Thread-1, current object com.huifer.design.singleton.nw.SimpleSingleton@63668bdb
Current thread Thread-0, current object com.huifer.design.singleton.nw.SimpleSingleton@63668bdb

Inner class

public class LazyInnerClassSingleton {
    private LazyInnerClassSingleton() {
    }

    public static LazyInnerClassSingleton getInstance() {
        return LazyObj.lazy;
    }

    private static class LazyObj {
        public static final LazyInnerClassSingleton lazy = new LazyInnerClassSingleton();

    }
}

Order of class loading

  1. Load LazyObj before loading LazyInnerClassSingleton
  2. The static variable called getInstance LazyObj has been initialized

attack

Reflection Attack

public class LazyInnerClassSingletonTest {

    public static void main(String[]args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<LazyInnerClassSingleton> clazz = LazyInnerClassSingleton.class;
        Constructor<LazyInnerClassSingleton> declaredConstructor = clazz.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        LazyInnerClassSingleton lazyInnerClassSingleton = declaredConstructor.newInstance(null);
        //output address
        System.out.println(lazyInnerClassSingleton);
        //output address
        System.out.println(LazyInnerClassSingleton.getInstance());
    }

}

com.huifer.design.singleton.nw.LazyInnerClassSingleton@6f94fa3e
com.huifer.design.singleton.nw.LazyInnerClassSingleton@5e481248
  • The simplest solution, construction is not allowed

          private LazyInnerClassSingleton() {
              throw new RuntimeException("ex");
          }

Serialization Attack

  • Hungry singleton

    public class SerializableSingleton implements Serializable {

      private static final SerializableSingleton singleton = new SerializableSingleton();
    
      private SerializableSingleton() {
      }
    public static SerializableSingleton getInstance() {
        return singleton;
    }
}
  • Attack code

    public class SerializableSingletonTest {

      public static void main(String[]args) {
          SerializableSingleton s1 = null;
          SerializableSingleton s2 = SerializableSingleton.getInstance();
          FileOutputStream fos = null;
          try {
              //write it out
              fos = new FileOutputStream("SerializableSingleton.obj");
              ObjectOutputStream oos = new ObjectOutputStream(fos);
              oos.writeObject(s2);
              oos.flush();
              oos.close();
    
              //read in
              FileInputStream fis = new FileInputStream("SerializableSingleton.obj");
              ObjectInputStream ois = new ObjectInputStream(fis);
    
              s1 =(SerializableSingleton) ois.readObject();
              ois.close();
    
              System.out.println(s1);
              System.out.println(s2);
    
          }
          catch(Exception e) {
              e.printStackTrace();
          }
      }

    }

  • Results of the

    com.huifer.design.singleton.nw.SerializableSingleton@6e8cf4c6
    com.huifer.design.singleton.nw.SerializableSingleton@355da254

  • readResolve method

      private Object readResolve() {
          return singleton;
      }
  • Why is this method useful?

  1. First of all, the way we read obj is ois.readObject()

    java.io.ObjectInputStream#readObject0

Insert picture description here

  • The following code in readObject0 continues to track

Insert picture description here

  • java.io.ObjectInputStream#readOrdinaryObject

Insert picture description here

Here desc.newInstance() creates one so it is not the same

Continue the same way down

Insert picture description here

  • Here is to determine whether there is readResolve method

    1. Execute this method if it exists, replace the result