Hungry Han style
Initialize the object at the beginning. Whether it is used or not, this class allocates space, initializes the object, and points the object to space
Disadvantages: waste of space
public class HungerMan { private HungerMan() { } private final static HungerMan hungerMan = new HungerMan(); private static HungerMan getInstance() { return hungerMan; } }
Lazy style
Use to initialize objects to save space
Disadvantages: it is only used for single thread. If the program wants to be concurrent, it needs to use double check lock
public class LazyMan { private LazyMan() { } private static LazyMan lazyMan ; private static LazyMan getInstance() { //The object is initialized only when it is empty if(lazyMan==null) { lazyMan = new LazyMan(); } return lazyMan; } public static void main(String[] args) { for(int i=0;i<10;i++) { new Thread(()->{ LazyMan.getInstance(); }).start(); } } }
Double check lock: add a layer of lock to the above code and use thread synchronization to synchronize
analysis:
- lazyMan = new LazyMan(); Not atomic operation
There are actually three steps:
*Allocate memory space
*Initialize constructor instance object
*Point the object to the space - Instruction rearrangement occurred
*The startup thread may execute 123 or 132
*If thread A executes 132, thread B first judges that the object is not null. Since the object execution order is not 123, the returned object is not initialized
Solution: add volatile
public class LazyMan { //Private constructor private LazyMan() { System.out.println(Thread.currentThread().getName()+"ok"); } //Plus volatile atomic operations private volatile static LazyMan lazyMan ; private static LazyMan getInstance() { /** * Use double check lock */ if(lazyMan==null) { synchronized (LazyMan.class) { if(lazyMan==null) { lazyMan = new LazyMan(); } } } return lazyMan; } public static void main(String[] args) { //1. for(int i=0;i<10;i++) { new Thread(()->{ LazyMan.getInstance(); }).start(); } } }
Static inner class
The static internal class is an independent layer, and the effect is the same as that of double check lock
package workspace.Singleton mode; //Static inner class public class Inside { private Inside() { System.out.println(Thread.currentThread().getName() + "ok"); } private static Inside getInstance() { return Man.INSIDE; } private static class Man { private final static Inside INSIDE = new Inside(); } public static void main(String[] args) { for (int i = 0; i < 10; i++) { new Thread(() -> { Inside.getInstance(); }).start(); } } }
Enumeration and reflection
Except enumeration, all methods are unsafe and can be destroyed by reflection. However, reflection is artificially destroyed. Using double retrieval or static internal classes is enough.
Reflection mechanism
The null parameter function can be constructed directly by reflection
public static void main(String[] args) throws Exception{ //Normal instantiation LazyMan lazyMan2 = LazyMan.getInstance(); System.out.println(lazyMan2); //Reflection failure Constructor<LazyMan> constructor = LazyMan.class.getDeclaredConstructor(null); constructor.setAccessible(true); LazyMan lazyMan = constructor.newInstance(); System.out.println(lazyMan); }
The results of the two objects are different, and the reflection is successfully destroyed
Solution: lock the null parameter function
private LazyMan() { synchronized (LazyMan.class) { if(lazyMan!=null) { throw new RuntimeException("Do not destroy the view by reflection"); } } }
Crack:
Both initialization objects get null parameter objects for reflection
combat
Flag key can be added to prevent reflection
However, this is still unsafe, because you can know jingQing by decompiling the code through jad, and modify it back to false. Reflection can still be broken.
Enumeration non reflective destruction
Through jad decompile enumeration, it can be found that there is no empty parameter constructor in enumeration, so enumeration cannot be destroyed.
Write test code
The returned result is not
It is not the result of reflection failure we want
Decompile
-
javap -p decompile results show that our constructor has parameters.
-
jad decompile source code is a parametric constructor.
Destroy it again
Reflection cannot break enumeration singleton mode