Singleton mode (hungry singleton, lazy singleton, DCL mode, static internal class implementation, enumeration Implementation) this article is enough!

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

  1. 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.

  2. 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);
}

Keywords: Java Singleton pattern

Added by ziesje on Thu, 10 Feb 2022 21:47:14 +0200