Fun thoroughly singleton

What is a singleton?

Ensure that category in memory can only have one object, and private construction.

Mainly to solve: a global classes used to create and destroy frequently.

  • 1, only one singleton class instance.
  • 2, singleton class must create their own unique instance.
  • 3, singleton class must provide this example to all other objects.

advantage:

  • 1, only one instance in memory, reducing memory overhead, especially frequent create and destroy instances (such as School of Management Home page cache).
  • 2. Avoid multiple assignment of resources (such as file write operations).

Cons: no interface, not inherited, and the conflict single responsibility principle, a class should only be concerned about the internal logic, not on the outside how to be instantiated.

Starving mode

[ Italy ] in the class loader instance is created,

[ Advantage ] multi-thread safe, loaded it in a class instance initialized

[ Shortcomings ] waste of resources

package com.singleton;

/**
 * 饿汉模式
 * 在类一加载的时候就创建了实列
 * 缺点是,会出现资源浪费
 */
public class Hungry {
    //资源浪费了
    Byte[] byte1 = new Byte[1024];
    Byte[] byte2 = new Byte[1024];
    Byte[] byte3 = new Byte[1024];
    Byte[] byte4 = new Byte[1024];

   private Hungry(){

   }

   private static Hungry hungry = new Hungry();

   public static Hungry getInstance(){
       return hungry;
   }
}

[ Note ] This single example is unsafe, we can use reflection to destroy this single case.

[ A reflective destruction ]

class test{
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    
Constructor<Hungry> declaredConstructor = Hungry.class.getDeclaredConstructor(null);
        //为true 表示可以获得私有成员
        declaredConstructor.setAccessible(true); 
         //没通过这个单例类u getInstance()方法 就创建了实例。
        Hungry hungry = declaredConstructor.newInstance();
    }
}

Lazy lazy mode and thread-safe mode

[ Italian example] when needed will create class

[ Advantage ] multithreading unsafe, plus synchronized so that multi-thread safe

[ Shortcomings ] must be locked synchronized to ensure a single case, but the lock will affect the efficiency. Performance getInstance () is not critical to the application (which uses less frequently)

package com.singleton;

/**
 * 当要使用的时候才创建实例
 */
public class Lazy {
     private Lazy(){
        System.out.println("线程"+Thread.currentThread().getName()+"创建了一个实例");
    }
    private static Lazy lazy ;

    //没有synchronized会出现线程安全问题,所以在方法上加上synchronized就线程安全了
    public static   Lazy getInstance(){
        if(lazy == null){
            lazy = new Lazy();
        }
        return  lazy;
    }
}

[ Simulated multi-threaded unsafe ]

class test2{
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new Thread(()->{
                Lazy.getInstance();
            },i+"").start();
        }
    }
}
/*输出:
线程1创建了一个实例
线程3创建了一个实例
线程4创建了一个实例
线程2创建了一个实例
线程0创建了一个实例
*/

Why does this happen?

Rearrangement instruction, and not create an instance of an atomic operation.

Creating order instance:

1, to allocate memory space

2, the constructor is executed, initializes the object

3, this object points to this memory space

To multi-thread, the thread will appear --A not perform complete, B thread to the discovery continue to create an object is null.

[ Note ] still use a single reflection to break this embodiment

[ A reflective destruction ]

class test2{
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        
   Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor(null);
        //为true 表示可以获得私有成员
        declaredConstructor.setAccessible(true); 
        //没通过这个单例 getInstance()方法 就获得了实例。
        Lazy lazy = declaredConstructor.newInstance();
        
    }
}

DCL lazy mode

What is DCL?

DCL also called double lock detection / double checking lock (DCL, i.e., double-checked locking).

That we give this single case of the double detection, and coupled with the lock. Prevent multithreading unsafe occur.

[ Advantage ] multi-thread safe

package com.singleton;

public class DCLLazy {
    private DCLLazy() {}
    //volatile防止指令重排,synchronized可以保证原子性
    private static volatile DCLLazy dclLazy = null;

    public static DCLLazy getInstance(){
        //如果这个实例不为空了其他线程都不能进来,还可以防止线程重复拿到锁,浪费资源
        if (dclLazy == null) {
            //当第一个线程进入后会给这个类上锁
            synchronized (DCLLazy.class) {
                if (dclLazy == null) {
                    dclLazy = new  DCLLazy();
                }
            }
        }
       return  dclLazy;
    }
}

[ Note ] this still can not prevent the damage for the singleton

[ A reflective destruction ]

class  test3{
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
       
Constructor<DCLLazy> declaredConstructor = DCLLazy.class.getDeclaredConstructor(null);
       DCLLazy dclLazy = declaredConstructor.newInstance();
       DCLLazy dclLazy2 = declaredConstructor.newInstance();
       System.out.println(dclLazy.equals(dclLazy2)); //输出false ,说明不是同一个对象
    }
}

So we have to strengthen this DCL, plus in construction

 private DCLLazy() {
  		 synchronized (DCLLazy.class) { //加锁,保证多线程下原子性
            if (judge == false) { //第一次创建实例为false,并修改judge为true
                judge = true;
            } else {
                throw new RuntimeException("不要使用反射来破坏");
            }
        }
    }

Run again the main method, discovered the error, the console appeared we throw an exception, it is not representative solve it? It is not, reflected energy and the acquired private attribute value change.

Caused by: java.lang.RuntimeException: 不要使用反射来破坏
	at com.singleton.DCLLazy.<init>(DCLLazy.java:15)
	... 6 more

Review reflection code:

class test3 {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        Field judge = DCLLazy.class.getDeclaredField("judge"); //获得属性
        judge.setAccessible(true);
Constructor<DCLLazy> declaredConstructor =DCLLazy.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        //创建了第一个实例
        DCLLazy dclLazy = declaredConstructor.newInstance(); 
        
		//出现把judge属性设为false,这样就能够继续创建实例了
        judge.set(declaredConstructor,false); 

        DCLLazy dclLazy2 = declaredConstructor.newInstance();
        //依然输出false ,说明依然不是同一个对象
        System.out.println(dclLazy.equals(dclLazy2)); 
    }
}

Static inner classes

Implements lazy loading, only when the internal class is called, will be loaded class loader.

[ Advantage ] multi-thread safe, this approach can achieve double check the lock mode the same effect, but for simpler

package com.singleton;

public class StaticSingleton {
    private StaticSingleton(){}

    public StaticSingleton getInstance(){
        return instanceClass.STATIC_SINGLETON;
    }

    //静态内部类
    public static class instanceClass{
   private  static final  StaticSingleton STATIC_SINGLETON = new StaticSingleton();
    }
}

[ Note ] still able to destroy this reflection Singleton

[ Reflecting damage ]

class test4{
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Constructor<StaticSingleton> declaredConstructor = StaticSingleton.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true); //为true 表示可以获得私有成员
        StaticSingleton ss = declaredConstructor.newInstance(); //没通过这个单例 getInstance()方法 就获得了实例。
    }
}

enumerate

[ Advantage ] This implementation has not been widely adopted, but it is the best method to achieve single-mode embodiment. It is more compact, automatic support serialization mechanism to prevent multiple instances of the absolute, multi-thread safe.

Description: This implementation has not been widely adopted, but this is the best way to achieve single-mode embodiment. It is more compact, automatic support serialization mechanism to prevent multiple instances of the absolute.
This approach is advocated Effective Java author Josh Bloch way, it can not only avoid multi-thread synchronization problems, but also automatically supports serialization mechanism to prevent deserialization re-create a new object, absolutely prevent multiple instantiation. However, only joined the enum properties after JDK1.5, write in this way can not help but feel strange, in practice, rarely used.
Not by reflection attack to call the private constructor.

[ Note ] reflection can be prevented.

public enum  EnumSingleton {
    INSTANCE;
     public void outPut(){
        System.out.println("红火火恍恍惚惚");
    }
}

Let's look at the class files compiled:

Found class file is compiled after a private constructor , then we can try to use reflection to call the constructor.

package li;

public enum EnumSingleton {
    INSTANCE;

    private EnumSingleton() {
    }

    public void outPut() {
        System.out.println("红火火恍恍惚惚");
    }
}

[ Use reflection to try destroy ]

class test5{
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        EnumSingleton enumSingleton = declaredConstructor.newInstance();
        enumSingleton.outPut();
    }
}

Found an error, tell us, do not have this construction method.

Exception in thread "main" java.lang.NoSuchMethodException: li.EnumSingleton.<init>()
	at java.lang.Class.getConstructor0(Class.java:3082)
	at java.lang.Class.getDeclaredConstructor(Class.java:2178)
	at li.test5.main(EnumSingleton.java:15)

We use jad to decompile, first open the jad, then enter cmd: jad -sjava EnumSingleton.class

Then came a java source file suffix:

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   EnumSingleton.java

package li;

import java.io.PrintStream;

public final class EnumSingleton extends Enum
{

    public static EnumSingleton[] values()
    {
        return (EnumSingleton[])$VALUES.clone();
    }

    public static EnumSingleton valueOf(String name)
    {
        return (EnumSingleton)Enum.valueOf(li/EnumSingleton, name);
    }
	
	//看这里,发现是有两个参数的有参构造方法
    private EnumSingleton(String s, int i)
    {
        super(s, i);
    }

    public void outPut()
    {
        System.out.println("\u7EA2\u706B\u706B\u604D\u604D\u60DA\u60DA");
    }

    public static final EnumSingleton INSTANCE;
    private static final EnumSingleton $VALUES[];

    static 
    {
        INSTANCE = new EnumSingleton("INSTANCE", 0);
        $VALUES = (new EnumSingleton[] {
            INSTANCE
        });
    }
}

Then we can use the reflection to see that there is a reference structure

class test5{
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //使用有参构造
 Constructor<EnumSingleton> declaredConstructor = 	      EnumSingleton.class.getDeclaredConstructor(String.class,int.class);
        declaredConstructor.setAccessible(true);
        
        EnumSingleton enumSingleton = declaredConstructor.newInstance();
        enumSingleton.outPut();
    }
}

He throws an exception: you can not tell enumerate to create objects using reflection

Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
	at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
	at li.test5.main(EnumSingleton.java:17)

to sum up

Under normal circumstances, does not recommend the use of the second lazy, it is recommended to use the first one kind of the way a hungry man. To achieve clear only when lazy loading effect, will use the first four kinds of static inner class way. If it comes to deserialize create an object, you can try using a fifth enumeration mode. If you have other special needs, you can consider using the first three kinds of double check the lock mode.

Published an original article · won praise 0 · Views 10

Guess you like

Origin blog.csdn.net/weixin_43988600/article/details/105184680