Introduction to singleton mode

The singleton pattern is one of the simplest, most basic, and most commonly used design patterns in Java. At run time, ensure that only one instance of a class is created, ensure that only one instance of a class is created, and provide a global access point to access it. Here are N ways to implement singleton patterns in Java.

The hungry type

public class Singleton {

    private static Singleton instance = new Singleton();

    private Singleton(a) {}public static Singleton getInstance(a) {
        returninstance; }}Copy the code

This is the simplest and most crude way to implement a safe singleton pattern, which we call hunger-handedness. It’s called hungry because the stomach is hungry and wants to eat right away, not wait for the birth time. This way, the Singleton instance is created when the class is loaded.

The disadvantage of the hanky-hanky style is that it is possible to create the instance before it is needed, so it is not lazy. The advantage is that the implementation is simple, and safe and reliable.

LanHanShi

public class Singleton {
    
    private static Singleton instance;

    private Singleton(a) {}public static Singleton getInstance(a) {
        if (instance == null) {
            instance = new Singleton();
        }
        returninstance; }}Copy the code

Slacker style is less “hungry” than hungrier style. Create instances only when you really need them. In the getInstance method, determining whether an instance is empty and then deciding whether to create it may seem perfect, but there are thread-safety issues. When obtaining instances concurrently, it is possible to build multiple instances. Therefore, this code needs to be improved.

public class SingletonSafe {

    private static volatile SingletonSafe singleton;

    private SingletonSafe(a) {}public static SingletonSafe getSingleton(a) {
        if (singleton == null) {
            synchronized (SingletonSafe.class) {
                if (singleton == null) {
                    singleton = newSingletonSafe(); }}}returnsingleton; }}Copy the code

Double check is used here to make the lazy singleton thread-safe. By locking, it is possible to ensure that only one thread goes into the second null-free code at the same time, thus ensuring that only one instance is created. The singleton is also referenced with the volatile keyword, whose key function is to prevent instruction reordering.

Static inner class

public class Singleton {

    private static class SingletonHolder {
        private static Singleton instance = new Singleton();
    }

    private Singleton(a) {}public static Singleton getInstance(a) {
        returnSingletonHolder.instance; }}Copy the code

Implementing the Singleton pattern through static inner classes is thread-safe, and the static inner classes are not loaded when the Singleton class is loaded, but when the getInstance() method is called, achieving lazy loading.

Static inner classes may seem like the perfect solution, but they are not. There may be reflection or deserialization attacks. Look at the following code:

public static void main(String[] args) throws Exception {
    Singleton singleton = Singleton.getInstance();
    Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
    constructor.setAccessible(true);
    Singleton newSingleton = constructor.newInstance();
    System.out.println(singleton == newSingleton);
}
Copy the code

Running results:

It turns out that the two instances are not the same, which violates the singleton rule.

In addition to reflection attacks, there may be deserialization attacks. As follows:

Introducing dependencies:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.8.1</version>
</dependency>
Copy the code

This dependency provides the serialization and deserialization utility classes.

The Singleton class implements the Java.io.Serializable interface.

As follows:

public class Singleton implements Serializable {

    private static class SingletonHolder {
        private static Singleton instance = new Singleton();
    }

    private Singleton(a) {}public static Singleton getInstance(a) {
        return SingletonHolder.instance;
    }

    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        byte[] serialize = SerializationUtils.serialize(instance); Singleton newInstance = SerializationUtils.deserialize(serialize); System.out.println(instance == newInstance); }}Copy the code

Running results:

The singleton pattern is implemented through enumeration

In Effective Java (a really good book), the best singleton implementation pattern is the enumeration pattern. Let the JVM help us with thread safety and single instance issues by taking advantage of enumerations. In addition, the writing method is very simple.

public enum Singleton {

    INSTANCE;

    public void doSomething(a) {
        System.out.println("doSomething"); }}Copy the code

Call method:

public class Main {

    public static void main(String[] args) { Singleton.INSTANCE.doSomething(); }}Copy the code

Directly through the Singleton. INSTANCE. DoSomething () call. Convenient, simple and safe.

conclusion

Several singleton modes are listed above, and their advantages and disadvantages are analyzed. At the same time, the best singleton writing method, enumeration mode, is also introduced. I believe that in the future, the singleton writing method of enumeration mode will be more and more popular.