1. Single case mode elements
Ensure that there is only one instance in a class and provide a global access point a.Private construction method b.Private static references point to their own instances c.Public static method with its own instance as return value
2. Hungry Han style single example
public class Hungry { //Private constructor to ensure that external classes cannot instantiate this class private Hungry (){} //Create your own instantiation of a class private final static Hungry HUNGRY= new Hungry (); //Create a get method and return an instance of HUNGRY public static Hungry getInstance(){ return HUNGRY; } }
2.1 characteristics of hungry Han style single case
-
Hungry Chinese style is a typical space for time. When a class is loaded, it will create a class instance. Whether you use it or not, you can create it first, and then you don't need to judge it every time you call it, which saves running time.
-
In terms of thread safety, the hungry Chinese style is thread safe, because the virtual machine is guaranteed to be loaded only once, and there will be no concurrency when loading classes.
3. Lazy single example
public class LazyMan{ private LazyMan(){ } private static LazyMan lazyMan; public static LazyMan getInstance(){ if(lazyMan==null){ lazyMan=new LazyMan(); } return lazyMan; } }
It is not difficult to find that lazy singleton is not safe when multithreading is concurrent, so it appears
4. Lazy single case double detection lock (DCL mode)
public class LazyMan{ private LazyMan(){ } private static LazyMan lazyMan; //Double detection lock public static LazyMan getInstance(){ if(lazyMan==null){ synchronized (LazyMan.class){ if(lazyMan==null){ lazyMan=new LazyMan(); } } } return lazyMan; } }
When lazyman is empty, add a lock on lazyman to ensure that there is only one class, but there may be problems in extreme cases of concurrency
//Double detection lock public static LazyMan getInstance(){ if(lazyMan==null){ synchronized (LazyMan.class){ if(lazyMan==null){ lazyMan=new LazyMan(); //Not atomic operation /** 1.Allocate memory space 2.Execute the construction method and initialize the object 3.Point this object to this space We expect the jvm to execute in the order of 123, But there will be instruction rearrangement here, jvm The execution sequence is 132 Suppose there is a thread, and the execution order is 1,2,3, When the execution reaches 2, the execution order of thread b is 1,3,2 At this time, when judging lazyMan, the result is not empty The return operation is then performed, but this is the lazyMan object it is questionable that */ } } } return lazyMan; }
To avoid instruction rearrangement, volatile is added
public class LazyMan{ private LazyMan(){ } private volatile static LazyMan lazyMan; //Double detection lock public static LazyMan getInstance(){ if(lazyMan==null){ synchronized (LazyMan.class){ if(lazyMan==null){ lazyMan=new LazyMan(); } } } return lazyMan; } }
5. Lazy singleton (static internal class implementation)
public class Holder{ private Holder(){ } public static Holder getInstace(){ return InnerClass.HOLDER; } public static class InnerClass{ private static final Holder HOLDER = new Holder(); } }
But the above lazy style is actually unsafe, because reflection can destroy the singleton mode
public class LazyMan{ private LazyMan(){ } private volatile static LazyMan lazyMan; //Double detection lock public static LazyMan getInstance(){ if(lazyMan==null){ synchronized (LazyMan.class){ if(lazyMan==null){ lazyMan=new LazyMan(); } } } return lazyMan; } //reflex public static void main (String[] args){ LazyMan instance1=LazyMan.getInstance(); Constructor<LazyMan> declaredConstructor=LazyMan.class.getDeclaredConstructor(null) //Destroy constructor privatization declaredConstructor.setAccessible(true); LazyMan instance2 =declaredConstructor.newInstance(); //The printed values are not the same, indicating that the singleton has been destroyed System.out.println(instance1); System.out.println(instance2); }
Of course, there are solutions to this problem
public class LazyMan{ private LazyMan(){ synchronized(LazyMan.class){ if(lazyMan!=null){ throw new RuntimeException("Do not use reflection breaking singleton exceptions") } private volatile static LazyMan lazyMan; //Double detection lock public static LazyMan getInstance(){ if(lazyMan==null){ synchronized (LazyMan.class){ if(lazyMan==null){ lazyMan=new LazyMan(); } } } return lazyMan; } //reflex public static void main (String[] args){ LazyMan instance1=LazyMan.getInstance(); Constructor<LazyMan> declaredConstructor=LazyMan.class.getDeclaredConstructor(null) //Destroy constructor privatization declaredConstructor.setAccessible(true); LazyMan instance2 =declaredConstructor.newInstance(); //Printing times error System.out.println(instance1); System.out.println(instance2); }
At this time, when the main method is executed again, an error will be reported and a runtime exception will be reported
If both instance objects are directly new
public class LazyMan{ private LazyMan(){ synchronized(LazyMan.class){ if(lazyMan!=null){ throw new RuntimeException("Do not use reflection breaking singleton exceptions") } private volatile static LazyMan lazyMan; //Double detection lock public static LazyMan getInstance(){ if(lazyMan==null){ synchronized (LazyMan.class){ if(lazyMan==null){ lazyMan=new LazyMan(); } } } return lazyMan; } //reflex public static void main (String[] args){ //LazyMan instance1=LazyMan.getInstance(); Constructor<LazyMan> declaredConstructor=LazyMan.class.getDeclaredConstructor(null) //Destroy constructor privatization declaredConstructor.setAccessible(true); LazyMan instance1 =declaredConstructor.newInstance(); LazyMan instance2 =declaredConstructor.newInstance(); //When printing, print different addresses -- > the single instance is destroyed again System.out.println(instance1); System.out.println(instance2); }
We found that a single case can still be destroyed
At this point, we upgrade again and define a variable that is assumed to be unknown to others
public class LazyMan{ //Define parameters, or key private static boolean flag=false; private LazyMan(){ synchronized(LazyMan.class){ if(flag==fasle){ flag=true; }else{ throw new RuntimeException("Do not use reflection breaking singleton exceptions") } } private volatile static LazyMan lazyMan; //Double detection lock public static LazyMan getInstance(){ if(lazyMan==null){ synchronized (LazyMan.class){ if(lazyMan==null){ lazyMan=new LazyMan(); } } } return lazyMan; } //reflex public static void main (String[] args){ //LazyMan instance1=LazyMan.getInstance(); Constructor<LazyMan> declaredConstructor=LazyMan.class.getDeclaredConstructor(null) //Destroy constructor privatization declaredConstructor.setAccessible(true); LazyMan instance1 =declaredConstructor.newInstance(); LazyMan instance2 =declaredConstructor.newInstance(); //Exception thrown during printing System.out.println(instance1); System.out.println(instance2); }
At this time, we find that it seems that the single instance will not be destroyed, but if someone breaks the flag field, the privatization of the flag field will fail
public class LazyMan{ //Define parameters, or key private static boolean flag=false; private LazyMan(){ synchronized(LazyMan.class){ if(flag==fasle){ flag=true; }else{ throw new RuntimeException("Do not use reflection breaking singleton exceptions") } } private volatile static LazyMan lazyMan; //Double detection lock public static LazyMan getInstance(){ if(lazyMan==null){ synchronized (LazyMan.class){ if(lazyMan==null){ lazyMan=new LazyMan(); } } } return lazyMan; } //reflex public static void main (String[] args){ //LazyMan instance1=LazyMan.getInstance(); Field flag =LazyMan.class.getDeclaredField("flag") //Invalidate privatization parameters flag.setAccessible(true); Constructor<LazyMan> declaredConstructor=LazyMan.class.getDeclaredConstructor(null) //Destroy constructor privatization declaredConstructor.setAccessible(true); LazyMan instance1 =declaredConstructor.newInstance(); //Change the flag value to false flag.set(instance1,false); LazyMan instance2 =declaredConstructor.newInstance(); //Exception thrown during printing System.out.println(instance1); System.out.println(instance2); }
After cracking the flag field, we found that the single case can still be destroyed
At this time, the enumeration class singleton mode appears
6. Lazy singleton (enumeration Implementation)
public enum EnumSingle{ INSTANCE; public EnumSingle getInstance(){ return INSTANCE; } class Test{ public static void main (String[] args){ EnumSingle instance1 = EnumSingle.INSTANCE; EnumSingle instance2 = EnumSingle.INSTANCE; //The printed values are the same System.out.println(instance1); System.out.println(instance2); }
At this time, we destroy the single case again through reflection
public enum EnumSingle{ INSTANCE; public EnumSingle getInstance(){ return INSTANCE; } class Test{ public static void main (String[] args) throws Exception{ EnumSingle instance1 =EnumSingle.INSTANCE; Constructor<EnumSingle> declaredConstructor=EnumSingle.class.getDeclaredConstructor(null) declaredConstructor.setAccessible(true); EnumSingle instance2=declaredConstructor.new Instance() //There is no null method argument in the enum class construct System.out.println(instance1); System.out.println(instance2); }
Decompile through jad tool
It is found that the constructor is parameterized, and the construction parameters are (String s, int i)
public enum EnumSingle{ INSTANCE; public EnumSingle getInstance(){ return INSTANCE; } class Test{ public static void main (String[] args) throws Exception{ EnumSingle instance1 =EnumSingle.INSTANCE; Constructor<EnumSingle> declaredConstructor=EnumSingle.class.getDeclaredConstructor(String.class,int.class) declaredConstructor.setAccessible(true); EnumSingle instance2=declaredConstructor.new Instance() //Error reporting cannot use reflection to destroy a single case System.out.println(instance1); System.out.println(instance2); }