Singleton mode of common design patterns

concept

Singleton Pattern (Singleton Pattern) is one of the simplest design patterns in Java. This type of design pattern is a creational pattern, which provides the best way to create objects.
This pattern involves a single class that is responsible for creating its own objects while ensuring that only a single object is created. This class provides a way to access its unique object, which can be accessed directly without instantiating objects of this class.

Common writing

Hungry singleton

Create a singleton when the singleton class is first loaded

public class HungerSingleton{
    
    
 	private static final HungerSingleton instance =new HungerSingleton();
    private HungerSingleton(){
    
    }
    public static HungerSingleton getInstance(){
    
    
        return instance;
    }
}

Disadvantage

  • Waste of memory space, not suitable for scenarios where a large number of singleton objects are created

Lazy singleton

In order to solve the problem of memory waste that may be caused by the hungry singleton, the lazy singleton is proposed. The characteristic of the lazy singleton is that the singleton object will be initialized only when it is used.

Thread unsafe

public class LazySingleton{
    
    
    private static LazySingleton instance =null;
    private LazySingleton(){
    
    }
    public static LazySingleton getInstance(){
    
    
        if(instance==null){
    
    
            return new LazySingleton();
        }
        return instance;
    }
}

Disadvantage

  • Thread unsafe

Thread safe

public class LazySingleton{
    
    
    private static LazySingleton instance =null;
    private LazySingleton(){
    
    }
    public static synchronized LazySingleton getInstance(){
    
    
        if(instance==null){
    
    
            return new LazySingleton();
        }
        return instance;
    }
}

Disadvantage

  • You must lock synchronized to ensure the singleton, but locking will affect efficiency.

Double check lock

It can not only solve thread safety but also improve the performance of the program, a double check lock is proposed

class SynchronizedSingleton{
    
    
    private static volatile SynchronizedSingleton instance =null;
    private SynchronizedSingleton(){
    
    }
    private static SynchronizedSingleton getInstance(){
    
    
        if (instance==null){
    
    
            synchronized (SynchronizedSingleton.class){
    
    
                if (instance==null){
    
    
                    return new SynchronizedSingleton();
                }
            }
        }
        return instance;
    }
}

Disadvantage

  • After all, the synchronized locking operation is still used, which affects performance

Internal class implementation

Utilizes the internal class loading method: the internal class will not be loaded when the external class is initialized, and the internal class will be initialized only when the method of the internal class is called.
It is a lazy singleton with the best performance and thread safety

class LazyInnerClassSingleton{
    
    
    private LazyInnerClassSingleton(){
    
    }

    public static final LazyInnerClassSingleton getInstance() {
    
    
        return LazyHoder.instance;
    }

    //延迟加载
    private static class LazyHoder{
    
    
        private static final LazyInnerClassSingleton instance=new LazyInnerClassSingleton();
    }
}

Reflection destruction singleton

But is the above method perfect? Let's first look at a test example

class LazyInnerClassSingletonTest{
    
    
    public static void main(String[] args) {
    
    

        try {
    
    
            Class<?>clazz=LazyInnerClassSingleton.class;
            Constructor c=clazz.getDeclaredConstructor(null);
             c.setAccessible(true);//强吻
             Object o1=c.newInstance();
             Object o2=LazyInnerClassSingleton.getInstance();
            System.out.println(o1==o2);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }
}

Operation result: It
Insert picture description here
can be seen that two different instances appeared when we called its construction method through reflection and called the getInstance method. How to solve this situation?

class LazyInnerClassSingleton{
    
    
    private LazyInnerClassSingleton(){
    
    
        if(LazyHoder.instance!=null){
    
    
            throw new RuntimeException("不允许创建多个实例");
        }
    }

    public static final LazyInnerClassSingleton getInstance() {
    
    
        return LazyHoder.instance;
    }

    //延迟加载
    private static class LazyHoder{
    
    
        private static final LazyInnerClassSingleton instance=new LazyInnerClassSingleton();
    }
}

Through the judgment and exception of the construction method, the caller can only get the instance through getInstance

Serialization destroy singleton

Once an object is created, sometimes it is necessary to serialize the object and write it to disk, and then read the object from the disk next time it is used and deserialize it to convert it into a memory object. The object after deserialization will reallocate memory, that is, recreate it. If the serialized object is a singleton object, it will destroy the singleton

public class HungerSingleton implements Serializable{
    
    
    private static final HungerSingleton instance =new HungerSingleton();
    private HungerSingleton(){
    
    }
    public static HungerSingleton getInstance(){
    
    
        return instance;
    }
    public static HungerSingleton deepClone(HungerSingleton hs){
    
    
        HungerSingleton i =null;
        try {
    
    
            //序列化对象
            FileOutputStream fo=new FileOutputStream("HungerSingleton.obj");
            ObjectOutputStream os=new ObjectOutputStream(fo);
            os.writeObject(hs);

            os.flush();
            os.close();

            //反序列化对象
            FileInputStream fi=new FileInputStream("HungerSingleton.obj");
            ObjectInputStream ois=new ObjectInputStream(fi);
            i=(HungerSingleton)ois.readObject();
            ois.close();

        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        return i;
    }

    public static void main(String[] args) {
    
    
        HungerSingleton i=HungerSingleton.getInstance();
        HungerSingleton ii=deepClone(i);
        System.out.println(i==ii);
    }
}

Operation result:
Insert picture description here
Obviously, the serialized object is inconsistent with the manually created object, and it was instantiated twice. So, how to solve this problem? It’s simple, just addreadResolveMethod is fine

public class HungerSingleton implements Serializable{
    
    
    private static final HungerSingleton instance =new HungerSingleton();
    private HungerSingleton(){
    
    }
    public static HungerSingleton getInstance(){
    
    
        return instance;
    }
     //序列化破坏单例的解决方案:
    //重写readResolve方法
    private Object readResolve(){
    
    
        return instance;
    }
}

Look at the results again:
Insert picture description here

Registered singleton

The registered singleton is also called the registered singleton, which means that each instance is registered in a certain place, and the singleton is obtained with a unique identifier

Enumerated singleton

enum EnumSingleton{
    
    
    INSTANCE;
    private Object obj;

    private Object getObj(){
    
    
        return obj;
    }
    public static EnumSingleton getInstance(){
    
    
        return INSTANCE;
    }

    public static void main(String[] args) {
    
    
        EnumSingleton e1=EnumSingleton.getInstance();
        EnumSingleton e2=EnumSingleton.getInstance();
        System.out.println(e1==e2);

    }
}

The enumeration singleton mode is a singleton mode recommended in the book "Effective Java"

Disadvantage

  • All objects are initialized in memory when the class is loaded. Like the hungry style, it is not suitable for scenarios where a large number of singleton objects are created.

ThreadLocal singleton

Earlier we also talked about ThreadLocal, which is unique to a single thread and is inherently thread-safe

class ThreadLocalSingleton{
    
    
    private ThreadLocalSingleton(){
    
    }

    private static final ThreadLocal<ThreadLocalSingleton> instance=new ThreadLocal<ThreadLocalSingleton>(){
    
    
        @Override
        protected ThreadLocalSingleton initialValue() {
    
    
            return new ThreadLocalSingleton();
        }
    };

    private static ThreadLocalSingleton getInstance(){
    
    
        return instance.get();
    }
}

ThreadLocal singleton can be regarded as a special singleton mode. If you are interested in the principle of ThreadLocal, you can read the previous article to understand ThreadLocal in depth.

to sum up

It is generally recommended to use the hungry Chinese singleton. Only when the lazy loading effect is to be clearly achieved, the singleton pattern implemented by the inner class will be used. If it involves deserialization to create an object, you can try to use the first enumeration method. If you have other special needs, you can consider using double lock detection.

Guess you like

Origin blog.csdn.net/xzw12138/article/details/106620163