The singleton pattern

  • Definition: Ensures that a class has only one instance and provides a global access point
  • Type: Create type
  • Useful scenarios: You want to make sure that there is absolutely only one instance in any case
  • Advantages:

    • Having only one instance in memory reduces memory overhead
    • You can avoid multiple utilizations of resources
    • Set global access points and strictly control access
  • Disadvantages: no interface, difficult to scale
  • Key points:

    • Private constructor
    • Thread safety
    • Lazy loading
    • Serialization and deserialization security
    • reflection
  • Double Check

  • Static inner classes:

  • Practical skills

    • decompiling
    • The memory principle
    • Multithreaded Debug

  • Related Design Patterns

    • Singleton mode and factory mode
    • Singleton pattern and element pattern
Code sample
LanHanShi
public class LazySingleton { private static LazySingleton lazySingleton; Private LazySingleton() {} /** * Synchronized (); /** * Synchronized (); * @Return */ public synchronized static LazySingleton getInstance(){if(LazySingleton ==null){ lazySingleton = new LazySingleton(); } return lazySingleton; }}
public class T implements Runnable{ @Override public void run() { LazySingleton lazySingleton = LazySingleton.getInstance(); System.out.println(Thread.currentThread()+" "+lazySingleton); }}

The test class:

public class Test { public static void main(String[] args) { // LazySingleton lazySingleton = LazySingleton.getInstance(); Thread t1 = new Thread(new T()); Thread t2 = new Thread(new T()); t1.start(); t2.start(); System.out.println("program end"); /* for (int i = 0; i < 10; i++) { new Thread(new T()).start(); * /}}}

Double check (improved) :

Public class LazyDoubleCheckSingleton {// Unallowed reordering of code for volatile multithreaded execution LazyDoubleCheckSingleton lazyDoubleCheckSingleton; Private LazyDoubleCheckSingleton() {} public static LazyDoubleCheckSingleton getInstance(){} public static LazyDoubleCheckSingleton getInstance() if(lazyDoubleCheckSingleton==null){ synchronized (LazyDoubleCheckSingleton.class){ if (lazyDoubleCheckSingleton==null){ lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton(); } } } return lazyDoubleCheckSingleton; }}

A lazy loading solution based on class initialization:

/ * * * the lazy loading based on class initialization solution * / public class StaticInnerClassSingleton {private StaticInnerClassSingleton () {} private static  class InnerClass{ private static StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton();  } public static StaticInnerClassSingleton getInstance(){ return InnerClass.staticInnerClassSingleton; }}
public class T implements Runnable{ @Override public void run() { // LazySingleton lazySingleton = LazySingleton.getInstance(); // LazyDoubleCheckSingleton lazyDoubleCheckSingleton = LazyDoubleCheckSingleton.getInstance(); StaticInnerClassSingleton staticInnerClassSingleton = StaticInnerClassSingleton.getInstance(); System.out.println(Thread.currentThread()+" "+staticInnerClassSingleton); }}
The hungry type
Public class HungrySingleton {public static HungrySingleton {public static HungrySingleton {public static HungrySingleton {public static HungrySingleton {public static HungrySingleton; static{ hungrySingleton = new HungrySingleton(); } private HungrySingleton() { } public static HungrySingleton getInstance() { return hungrySingleton; }}
Serialization breaks the singleton pattern
Public class HungrySingleton implements Serializable {public class HungrySingleton implements Serializable {private static final; public class HungrySingleton implements Serializable {private static final HungrySingleton hungrySingleton; static{ hungrySingleton = new HungrySingleton(); } private HungrySingleton() { } public static HungrySingleton getInstance() { return hungrySingleton; } /** * If you do not write this method, deserialization will create a new object through reflection and return it, which is not the same object as the serialized object. * If this method is written, deserialization will return a new object even though it is created through reflection. * @return */ private Object readResolve(){ return hungrySingleton; }}
public class Test { public static void main(String[] args) throws IOException, ClassNotFoundException { HungrySingleton instance = HungrySingleton.getInstance(); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file")); oos.writeObject(instance); File file = new File("singleton_file"); ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file)); HungrySingleton newInstance = (HungrySingleton) ois.readObject(); System.out.println(instance); System.out.println(newInstance); System.out.println(instance==newInstance); }}
Reflection attack solution
Public class HungrySingleton implements Serializable {public class HungrySingleton implements Serializable {private static final; public class HungrySingleton implements Serializable {private static final HungrySingleton hungrySingleton; static{ hungrySingleton = new HungrySingleton(); } private HungrySingleton() { if (hungrySingleton! =null){throw new RuntimeException(" Singleton constructor disallows reflection calls "); } } public static HungrySingleton getInstance() { return hungrySingleton; }}
public class StaticInnerClassSingleton { private StaticInnerClassSingleton() { if (InnerClass.staticInnerClassSingleton! =null){throw new RuntimeException(" Singleton constructor disallows reflection calls "); } } private static class InnerClass{ private static StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton(); } public static StaticInnerClassSingleton getInstance(){ return InnerClass.staticInnerClassSingleton; }}
public class LazySingleton { private static LazySingleton lazySingleton; private LazySingleton() { if (lazySingleton! =null){throw new RuntimeException(" Singleton constructor disallows reflection calls "); }} public synchronized static LazySingleton getInstance(){if(LazySingleton ==null){LazySingleton = new LazySingleton(); } return lazySingleton; }}

The test class:

/** * can solve Hungry Reflex attack, but not Lazy Reflex attack. */ public class Test { public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { // Class hungrySingletonClass = HungrySingleton.class; // Class objectClass = StaticInnerClassSingleton.class; Class objectClass = LazySingleton.class; Constructor constructor = objectClass.getDeclaredConstructor(); constructor.setAccessible(true); // HungrySingleton instance = HungrySingleton.getInstance(); // HungrySingleton newInstance = (HungrySingleton) constructor.newInstance(); // StaticInnerClassSingleton instance = StaticInnerClassSingleton.getInstance(); // StaticInnerClassSingleton newInstance = (StaticInnerClassSingleton) constructor.newInstance(); LazySingleton newInstance = (LazySingleton) constructor.newInstance(); LazySingleton instance = LazySingleton.getInstance(); System.out.println(instance); System.out.println(newInstance); System.out.println(instance==newInstance); }}
Enum enumerates singletons

JAD: Decompiler tool

serialization

public class Test { public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { // HungrySingleton instance = HungrySingleton.getInstance(); EnumInstance instance = EnumInstance.getInstance(); instance.setData(new Object()); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file")); oos.writeObject(instance); File file = new File("singleton_file"); ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file)); // HungrySingleton newInstance = (HungrySingleton) ois.readObject(); EnumInstance newInstance = (EnumInstance) ois.readObject(); System.out.println(instance.getData()); System.out.println(newInstance.getData()); System.out.println(instance.getData()==newInstance.getData()); }}

reflection

public enum EnumInstance { INSTANCE{ protected void printTest(){ System.out.println("Feyl Print Test"); }}; protected abstract void printTest(); private Object data; public Object getData() { return data; } public void setData(Object data) { this.data = data; } public static EnumInstance getInstance(){ return INSTANCE; }}
public class Test { public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { Class objectClass = EnumInstance.class; Constructor constructor = objectClass.getDeclaredConstructor(String.class,int.class); constructor.setAccessible(true); EnumInstance instance = (EnumInstance) constructor.newInstance("Feyl",666); // EnumInstance instance = EnumInstance.getInstance(); // instance.printTest(); }}
Container singleton
Public class ContainerSingleton {// Thread insecure private static Map<String,Object bb0 SingletonMap = new HashMap<>(); private ContainerSingleton() { } public static void putInstance(String key, Object instance){ if (! StringUtils.isNotBlank(key)&&instance! =null){ if(! singletonMap.containsKey(key)){ singletonMap.put(key,instance); } } } public static Object getInstance(String key){ return singletonMap.get(key); }}
public class T implements Runnable{
    @Override
    public void run() {
        ContainerSingleton.putInstance("object",new Object());
        Object instance = ContainerSingleton.getInstance("object");
        System.out.println(Thread.currentThread()+" "+instance);
    }
}

The test class:

public class Test { public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { Thread t1 = new Thread(new T()); Thread t2 = new Thread(new T()); t1.start(); t2.start(); System.out.println("program end"); }}
A singleton of the TreadLocal thread
public class ThreadLocalInstance { private static final ThreadLocal<ThreadLocalInstance> threadLocalInstance = new ThreadLocal<ThreadLocalInstance>(){ @Override protected ThreadLocalInstance initialValue() { return new ThreadLocalInstance(); }}; private ThreadLocalInstance(){ } public static ThreadLocalInstance getInstance() { return threadLocalInstance.get(); }}
public class T implements Runnable{ @Override public void run() { ThreadLocalInstance instance = ThreadLocalInstance.getInstance(); System.out.println(Thread.currentThread()+" "+instance); }}

The test is similar to the above

“Space for time” : ThreadLocal creates a space for each thread, and each thread gets its own unique object.