Singleton pattern of Java design pattern

Singleton mode (singleton pattern) is one of the simplest design patterns in Java. This type of design pattern belongs to the creation pattern, which provides the best way to create objects. This pattern involves a single class, which is responsible for creating its own objects and ensuring that only a single object is created. This class provides a way to access its unique objects, It can be accessed directly without instantiating the object of this class. Note: 1. A singleton class can only have one instance. 2. A singleton class must create its own unique instance. 3. A singleton class must provide this instance to all other objects.

introduce

intention

Ensure that a class has only one instance and provide a global access point to access it.

Main solution

A globally used class is frequently created and destroyed.

When to use

When you want to control the number of instances and save system resources.

How to solve

Judge whether the system already has this singleton. If so, return. If not, create it.

critical code

Constructors are private.

Application examples

1. There is only one head teacher in a class. 2. Windows is multi process and multi-threaded. When operating a file, it is inevitable that multiple processes or threads operate a file at the same time, so all files must be processed through a unique instance. 3. Some device managers are often designed in singleton mode. For example, a computer has two printers, which must be processed when outputting. Two printers cannot print the same file.

advantage

1. There is only one instance in the memory, which reduces the memory overhead, especially the frequent creation and destruction of instances (such as the homepage page cache of the school of management). 2. Avoid multiple occupation of resources (such as file writing).

shortcoming

There is no interface, no inheritance, and it conflicts with the principle of single responsibility. A class should only care about the internal logic, not how to instantiate it outside.

Usage scenario

1. A unique serial number is required for production. 2. The counters in the web do not need to be added to the database every time they are refreshed, but are cached first with a single instance. 3. Creating an object consumes too many resources, such as the connection between I / O and database.

matters needing attention

The getInstance () method needs to use the synchronization lock synchronized (singleton. Class) to prevent multiple threads from entering at the same time, causing the instance to be instantiated multiple times.

realization

We will create a singleobject class. The singleobject class has its private constructor and a static instance of itself. The singleobject class provides a static method for the outside world to obtain its static instance. Singletonpatterndemo, our demo class uses the singleobject class to get the singleobject object object.

Step 1

Create a singleton class.

public class SingleObject {
 
   //创建 SingleObject 的一个对象
   private static SingleObject instance = new SingleObject();
 
   //让构造函数为 private,这样该类就不会被实例化
   private SingleObject(){}
 
   //获取唯一可用的对象
   public static SingleObject getInstance(){
      return instance;
   }
 
   public void showMessage(){
      System.out.println("Hello World!");
   }
}

Step 2

Get the unique object from the singleton class.

public class SingletonPatternDemo {
   public static void main(String[] args) {
 
      //不合法的构造函数
      //编译时错误:构造函数 SingleObject() 是不可见的
      //SingleObject object = new SingleObject();
 
      //获取唯一可用的对象
      SingleObject object = SingleObject.getInstance();
 
      //显示消息
      object.showMessage();
   }
}

Step 3

Execute the program and output the results:

Hello World!

Several implementation methods of singleton mode

There are many ways to implement the singleton mode, as shown below:

1. Lazy, thread unsafe

Lazy initialization: Yes, it is safe to multithread: No, it is difficult to implement: easy to describe: this method is the most basic implementation method. The biggest problem of this implementation is that it does not support multithreading. Because there is no lock synchronized, it is not strictly a singleton mode. This method of lazy loading obviously does not require thread safety and cannot work normally in multithreading.

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

2. Lazy, thread safe

Lazy initialization: Yes, it is safe for multithreading: Yes, it is difficult to implement: easy to describe: this method has good lazy loading and can work well in multithreading. However, the efficiency is very low, and synchronization is not required in 99% of cases. Advantages: initialize only after the first call to avoid memory waste. Disadvantages: you must lock synchronized to ensure single instance, but locking will affect efficiency. The performance of getInstance () is not critical to the application (this method is used less frequently).

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

3. Hungry Han style

Lazy initialization: No, multithreading safety: Yes, implementation difficulty: easy to describe: this method is commonly used, but it is easy to generate garbage objects. Advantages: without locking, the execution efficiency will be improved. Disadvantages: class initialization when loading, wasting memory. Based on the classloader mechanism, it avoids the synchronization problem of multiple threads. However, instance is instantiated when class loading. Although there are many reasons for class loading, most of them call getInstance method in singleton mode, However, it is not certain that there are other ways (or other static methods) to cause class loading. At this time, initializing instance obviously does not achieve the effect of lazy loading.

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

4. Double checked lock (DCL, double checked locking)

JDK version: jdk1 5. Whether lazy is initialized: Yes, whether multithreading is safe: Yes, implementation difficulty: more complex Description: this method adopts double lock mechanism, which is safe and can maintain high performance in the case of multithreading. The performance of getInstance () is critical to the application.

public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
    if (singleton == null) {  
        synchronized (Singleton.class) {  
        if (singleton == null) {  
            singleton = new Singleton();  
        }  
        }  
    }  
    return singleton;  
    }  
}

5. Registered / static inner class

Lazy initialization: Yes, multi thread safety: Yes, implementation difficulty: General Description: this method can achieve the same effect as the double check lock method, but the implementation is simpler. When using deferred initialization for static domains, this should be used instead of double check locking. This method is only applicable to the static domain. The double check lock method can be used when the instance domain needs to delay initialization. This method also uses the classloader mechanism to ensure that there is only one thread when initializing the instance. It is different from the third method: in the third method, as long as the singleton class is loaded, Then instance will be instantiated (the lazy loading effect is not achieved). In this way, the singleton class is loaded, and the instance is not necessarily initialized. Because the singletonholder class is not actively used, the singletonholder class will be explicitly loaded and the instance will be instantiated only by explicitly calling the getInstance method. Imagine that if instantiating an instance consumes a lot of resources In order to delay the loading, on the other hand, you don't want to instantiate the singleton class when it is loaded, because you can't ensure that the singleton class may be actively used in other places to be loaded, so it's obviously inappropriate to instantiate instance at this time. At this time, this method is very reasonable compared with the third method.

public class Singleton {  
    private static class SingletonHolder {  
    private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
    return SingletonHolder.INSTANCE;  
    }  
}

6. Enumeration

JDK version: jdk1 5. Whether lazy is initialized: No, whether multithreading is safe: Yes, implementation difficulty: easy to describe: this implementation method has not been widely used, but it is the best way to implement singleton mode. It is more concise, automatically supports serialization mechanism and absolutely prevents multiple instantiations. This method is advocated by effective Java author Josh Bloch. It can not only avoid the problem of multi-threaded synchronization, but also automatically support the serialization mechanism to prevent deserialization, re create new objects, and absolutely prevent multiple instantiations. However, due to jdk1 The enum feature was added after 5. It feels strange to write in this way, and it is rarely used in practical work. Private constructor cannot be called through reflection attack.

public enum Singleton {  
    INSTANCE;  
    public void whateverMethod() {  
    }  
}

wise remark of an experienced person

Generally, the first and second lazy ways are not recommended, and the third hungry way is recommended. The fifth registration method is only used when the lazy loading effect is to be clearly realized. If it involves deserialization to create objects, you can try the sixth enumeration method. If there are other special requirements, the fourth double check lock mode can be considered. At present, the best way is to use enumeration, because other methods do not prevent reflection. Enumeration can prevent reflection.

Creating objects with reflections

public static void main(String[] args) throws Exception {
        Singleton s=Singleton.getInstance();
        Singleton sual=Singleton.getInstance();
        Constructor<Singleton> constructor=Singleton.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        Singleton s2=constructor.newInstance();
        System.out.println(s+"\n"+sual+"\n"+s2);
        System.out.println("正常情况下,实例化两个实例是否相同:"+(s==sual));
        System.out.println("通过反射攻击单例模式情况下,实例化两个实例是否相同:"+(s==s2));
    }

The results are as follows:

cn.singleton.Singleton@1641e19d
cn.singleton.Singleton@1641e19d
cn.singleton.Singleton@677323b6
正常情况下,实例化两个实例是否相同:true
通过反射攻击单例模式情况下,实例化两个实例是否相同:false

Let's examine the singleton pattern of enumeration

public static void main(String[] args) throws Exception{
        EnumSingleton singleton1=EnumSingleton.INSTANCE;
        EnumSingleton singleton2=EnumSingleton.INSTANCE;
        System.out.println("正常情况下,实例化两个实例是否相同:"+(singleton1==singleton2));
        Constructor<EnumSingleton> constructor= null;
        constructor = EnumSingleton.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        EnumSingleton singleton3= null;
        singleton3 = constructor.newInstance();
        System.out.println(singleton1+"\n"+singleton2+"\n"+singleton3);
        System.out.println("通过反射攻击单例模式情况下,实例化两个实例是否相同:"+(singleton1==singleton3));
    }

The result will report exception in thread "main" Java lang.NoSuchMethodException。 The reason for this exception is enumsingleton class. Getdeclaraedconstructors () gets all constructors and finds that there is no parameterless constructor we set, There is only one constructor whose parameter is (string. Class, int.class). When creating an object through newinstance, it will check whether the class is enum decorated. If so, an exception will be thrown and the reflection fails. Therefore, enumeration is not afraid of launching attacks. Newinstance method source code:

public T newInstance(Object ... initargs)
        throws InstantiationException,illegalaccessexception,IllegalArgumentException,InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz,modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller,clazz,null,modifiers);
            }
        }
        if ((clazz.getModifiers() & Modifier.ENUM) != 0)
            throw new IllegalArgumentException("Cannot reflectively create enum objects");
        ConstructorAccessor ca = constructorAccessor;   // read volatile
        if (ca == null) {
            ca = acquireConstructorAccessor();
        }
        @SuppressWarnings("unchecked")
        T inst = (T) ca.newInstance(initargs);
        return inst;
    }
The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>