1. Definitions
- Singleton Pattern refers to ensuring that a class has absolutely only one instance in any case and providing a global access point.
- Hide all its construction methods
- It belongs to creation mode
2. Applicable scenarios
Make sure there is absolutely only one instance in any case
For example: ServletContext, ServletConfig, ApplicationContext, DBPool
3. Common writing methods of singleton mode
3.1 starving singleton (create an instance when the singleton class is loaded for the first time)
advantage
High execution efficiency, high performance and no lock
shortcoming
Class is initialized when it is loaded. In some cases, it may cause a waste of memory, because it will be initialized whether it is used or not. What is not wasted is that it is initialized when it is used. Therefore, when it is used in large quantities, it will waste memory by using hungry man style single instance
Case code
/** * Advantages: high execution efficiency, high performance and no lock * Disadvantages: class is initialized when it is loaded, which may cause memory waste in some cases, * Because it will be initialized whether it is used or not, what memory is not wasted is to initialize when it is used * Therefore, when it is used in large quantities, using hungry man style singleton will waste memory */ public class HungrySingleton { // static is initialized when the class is loaded private static final HungrySingleton hungrySingleton = new HungrySingleton(); private HungrySingleton(){ } public HungrySingleton getInstance(){ return hungrySingleton; } }
- static state
public class HungryStaticSingleton { //Class loading order //Static before dynamic //First up, then down //Attribute before method private static final HungryStaticSingleton hungryStaticSingleton; //It doesn't make any difference. Install B static { hungryStaticSingleton = new HungryStaticSingleton(); } private HungryStaticSingleton(){ } public HungryStaticSingleton getInstance(){ return hungryStaticSingleton; } }
3.2 lazy singleton (an instance is created only when it is called externally)
advantage
Advantages: save memory
shortcoming
Disadvantages: threads are unsafe. Two threads may call this instance
case
/** * Advantages: save memory * Disadvantages: threads are unsafe. Two threads may call this instance */ public class LazySimpleSingleton { private static LazySimpleSingleton lazySimpleSingleton ; // Privatization construction method private LazySimpleSingleton(){ } // Provide a global access point public synchronized static LazySimpleSingleton getInstance(){ if (lazySimpleSingleton == null){ lazySimpleSingleton = new LazySimpleSingleton(); } return lazySimpleSingleton; } }
The authentication thread is unsafe
- One thread
/** * One thread */ public class ExectorThread implements Runnable{ @Override public void run() { LazySimpleSingleton lazySimpleSingleton = LazySimpleSingleton.getInstance(); System.out.println(Thread.currentThread().getName() + ":" +lazySimpleSingleton); } }
- test
public class LazySimpleSingletonTest { public static void main(String[] args) { Thread t1 = new Thread(new ExectorThread()); Thread t2 = new Thread(new ExectorThread()); t1.start(); t2.start(); System.out.println("End"); } }
Operation results
- Same instance
1. Execute in normal sequence
2. The latter covers the former - Different instances
Enter the conditions at the same time and return in order
Solve thread insecurity - lock (let one thread execute and then another thread execute)
/** * Advantages: save memory * Disadvantages: threads are unsafe. Two threads may call this instance * * Solve thread insecurity - > synchronized locking * ->New disadvantage - bottleneck, performance affected */ public class LazySimpleSingleton { private static LazySimpleSingleton lazySimpleSingleton ; // Privatization construction method private LazySimpleSingleton(){ } // Provide a global access point // Lock to solve thread insecurity public synchronized static LazySimpleSingleton getInstance(){ if (lazySimpleSingleton == null){ lazySimpleSingleton = new LazySimpleSingleton(); } return lazySimpleSingleton; } }
The impact of locking - a new drawback - is the bottleneck and performance is affected
For example, high-speed railway station entrance inspection
Double check lock solves the disadvantage of locking
/** * Double lock * Advantages: high performance and thread safety * Disadvantages: readability is more difficult and not elegant enough */ public class LazyDoubleCheckSingleton { // Double check lock will cause the problem of instruction reordering. To solve it, add volatile private volatile static LazyDoubleCheckSingleton lazyDoubleCheckSingleton; private LazyDoubleCheckSingleton(){ } public static LazyDoubleCheckSingleton getInstance(){ // Check for blockage if (lazyDoubleCheckSingleton == null){ synchronized (LazyDoubleCheckSingleton.class){ // Check whether you want to recreate the instance if (lazyDoubleCheckSingleton == null){ lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton(); } } } return lazyDoubleCheckSingleton; } }
Solve the disadvantage of not elegant double check lock (static internal class writing)
/** * It looks like a hungry man, but it's actually a lazy man * Load - > lazystatic innerclasssingleton Class starts loading * LazyStaticInnerClassSingleton$LazyHolder.class Only return lazyholder INSTANCE; Was loaded * * Advantages: elegant writing, using Java's own syntax characteristics, high performance and avoiding memory waste * Disadvantages: can be destroyed by reflection 1 */ public class LazyStaticInnerClassSingleton { private LazyStaticInnerClassSingleton(){ } private static LazyStaticInnerClassSingleton getInstance(){ return LazyHolder.INSTANCE; } /** * Static inner class */ private static class LazyHolder{ private static final LazyStaticInnerClassSingleton INSTANCE = new LazyStaticInnerClassSingleton(); } }
Restore the scene of reflection destruction singleton
/** * Destroyed a single case */ public class ReflectTest { public static void main(String[] args) { try { Class<?> clazz = LazyStaticInnerClassSingleton.class; Constructor<?> c = clazz.getDeclaredConstructor(null); // Because the constructor is private, you can ignore private settings here c.setAccessible(true); Object instance1 = c.newInstance(); Object instance2 = c.newInstance(); System.out.println(instance1); System.out.println(instance2); System.out.println(instance1 == instance2); } catch (Exception e) { e.printStackTrace(); } } }
Scenario of solving single case of reflection destruction
/** * It looks like a hungry man, but it's actually a lazy man * Load - > lazystatic innerclasssingleton Class starts loading * LazyStaticInnerClassSingleton$LazyHolder.class Only return lazyholder INSTANCE; Was loaded * * Advantages: elegant writing, using Java's own syntax characteristics, high performance, avoiding memory waste and can not be destroyed by reflection * Disadvantages: the construction method is not elegant enough */ public class LazyStaticInnerClassSingleton { private LazyStaticInnerClassSingleton(){ if (LazyHolder.INSTANCE != null){ throw new RuntimeException("Illegal access"); } } private static LazyStaticInnerClassSingleton getInstance(){ return LazyHolder.INSTANCE; } /** * Static inner class */ private static class LazyHolder{ private static final LazyStaticInnerClassSingleton INSTANCE = new LazyStaticInnerClassSingleton(); } }
3.3 registered singleton (CACHE each instance in a unified container and obtain the instance with a unique identifier)
Enumerative case code
/** * Very elegant * But because INSTANCE is declared from the beginning, it is similar to the hungry man style * It is not suitable for use in large quantities * Next, it's up to spring IOC */ public enum EnumSingleton { INSTANCE; private Object data; public Object getData() { return data; } public void setData(Object data) { this.data = data; } // Provide a global access point public static EnumSingleton getInstance(){ return INSTANCE; } }
Reflection test
/** *Enumeration cannot create an instance */ public class EnumSingletonTest { public static void main(String[] args) { // EnumSingleton enumSingleton = EnumSingleton.getInstance(); // enumSingleton.setData(new Object()); try { Class clazz = EnumSingleton.class; Constructor c = clazz.getDeclaredConstructor(String.class, int.class); // setAccessible is required because it is private c.setAccessible(true); Object o = c.newInstance(); System.out.println(o); }catch (Exception e){ e.printStackTrace(); } } }
result
Container to solve the problem of single case in mass production
Because enumeration is not suitable for mass production, you can use container to solve mass production singletons, using Spring
public class ContainerSingleton { private ContainerSingleton(){ } private static Map<String,Object> ioc = new ConcurrentHashMap<String, Object>(); public static Object getInstance(String className){ Object instance = null; if (!ioc.containsKey(className)){ try { instance = Class.forName(className).newInstance(); ioc.put(className,instance); }catch (Exception e){ e.printStackTrace(); } return instance; }else { return ioc.get(className); } } }
- test
public class ContainerSingletonTest { public static void main(String[] args) { Object instance1 = ContainerSingleton.getInstance("com.DesignLearn.SingletonPattern.test.pojo"); Object instance2 = ContainerSingleton.getInstance("com.DesignLearn.SingletonPattern.test.pojo"); System.out.println(instance1); System.out.println(instance2); System.out.println(instance1 == instance2); } }
Restore the scenario of deserialization breaking singleton
-
serialize
Converts the state of an object in memory into bytecode
Write bytecode to disk through IO output stream
Permanent preservation -
Deserialization
Read the persistent bytecode content into memory through IO input stream
Convert to a Java object -
Case code
public class SeriableSingleton implements Serializable { // serialize // Converts the state of an object in memory into bytecode // Write bytecode to disk through IO output stream // Permanent preservation // Deserialization // Read the persistent bytecode content into memory through IO input stream // Convert to a Java object private static SeriableSingleton seriableSingleton = new SeriableSingleton(); private SeriableSingleton(){ } public static SeriableSingleton getInstance(){ return seriableSingleton; } }
- test
public class SeriableSingletonTest { public static void main(String[] args) { SeriableSingleton s1 = null; SeriableSingleton s2 = SeriableSingleton.getInstance(); FileOutputStream fos = null; try{ fos = new FileOutputStream("SeriableSingleton.obj"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(s2); oos.flush(); oos.close(); FileInputStream fis = new FileInputStream("SeriableSingleton.obj"); ObjectInputStream ois = new ObjectInputStream(fis); s1 =(SeriableSingleton) ois.readObject(); ois.close(); System.out.println(s1); System.out.println(s2); System.out.println(s1 == s2); }catch (Exception e){ e.printStackTrace(); } } }
-
result
The result should be the same -
resolvent
public class SeriableSingleton implements Serializable { // serialize // Converts the state of an object in memory into bytecode // Write bytecode to disk through IO output stream // Permanent preservation // Deserialization // Read the persistent bytecode content into memory through IO input stream // Convert to a Java object private static SeriableSingleton seriableSingleton = new SeriableSingleton(); private SeriableSingleton(){ } public static SeriableSingleton getInstance(){ return seriableSingleton; } // Solve the serialization problem private Object readResolve(){ return seriableSingleton; } }
3.4 ThreadLocal singleton (ensuring global uniqueness within threads and inherent thread safety)
Case code
- ThreadLocalSingleton
public class ThreadLocalSingleton { private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance = new ThreadLocal<ThreadLocalSingleton>(){ @Override protected ThreadLocalSingleton initialValue() { return new ThreadLocalSingleton(); } }; private ThreadLocalSingleton(){ } public static ThreadLocalSingleton getInstance(){ return threadLocalInstance.get(); } }
- Test ThreadLocalSingletonTest
public class ThreadLocalSingletonTest { public static void main(String[] args) { System.out.println(ThreadLocalSingleton.getInstance()); System.out.println(ThreadLocalSingleton.getInstance()); System.out.println(ThreadLocalSingleton.getInstance()); Thread t1 = new Thread(new ExectorThread()); Thread t2 = new Thread(new ExectorThread()); t1.start(); t2.start(); System.out.println("End"); } }
- Test execorthread
/** * One thread */ public class ExectorThread implements Runnable{ @Override public void run() { ThreadLocalSingleton threadLocalSingleton = ThreadLocalSingleton.getInstance(); System.out.println(ThreadLocalSingleton.getInstance()); System.out.println(Thread.currentThread().getName() + ":" +threadLocalSingleton); } }
4. Summary
Advantages of singleton mode
- There is only one instance in memory
- Multiple utilization of resources can be avoided
- Set global access points to strictly control access
Disadvantages of singleton mode
- Without interface, it is difficult to expand
- If you want to extend the singleton object, you can only modify the code, and there is no other way
Key summary
- Privatization constructor
- Ensure thread safety
- Delayed loading
- Prevent serialization and deserialization from destroying singletons
- Single case of defensive reflex attack