Thoroughly understand the singleton mode [one piece is enough]

1. [Hungry Man Mode]-Multi-thread safe singleton mode example 1 (not using synchronization lock)

Disadvantage: The object is initialized before it is used. This may bring potential performance problems: What if the object is large? It is a huge waste to load this object into memory before using it. In addition, when there are many such types in the system, it will slow down the startup speed.

public class EagerSingleton {
    private static final EagerSingleton singleton = new EagerSingleton();

    private EagerSingleton() {
    }

    public static EagerSingleton getSingleton() {
        return singleton;
    }
}

2. [Lazy man mode] Multi-thread safe singleton mode example 2 (using synchronization method)

Disadvantages: The entire method is locked at one time, and the granularity is somewhat large. Improvement-only lock the instantiation statement

public class LazySingleton {
    private static LazySingleton singleton = null;

    private LazySingleton() {
    }

    public static synchronized LazySingleton getSingleton() {
        if (singleton == null)
            singleton = new LazySingleton();
        return singleton;
    }
}

Three, [lazy man mode] multi-threaded safety singleton mode example three (using synchronization method-double-checked lock Double-Checked Lock)

Reasons for double checking: There may be multiple threads simultaneously synchronizing if judgment statements outside the block. Failure to double check will result in multiple instances being created.

At the same time, because singleton = new LazySingleton() is not an atomic operation, it is divided into 3 steps:

①Allocate memory to the singleton ②Create an object in the space that is initialized ③Point the singleton variable to the allocated memory space

However, there is an optimization of instruction reordering in the JVM just-in-time compiler. That is to say, the order of ② and ③ above cannot be guaranteed, and the final execution order may be ①②③ or ①③②. If it is the latter, it is preempted by thread two before ③ is executed and ② is not executed. At this time, instance is already non-null (but not initialized), so thread two will directly return to instance, then use, and then Report an error.
The solution is to declare the singleton variable as volatile , because volatile has the characteristic of prohibiting instruction rearrangement.

Synchronized can guarantee the order of the program (that is, the order of execution of the program is executed in the order of the code), but it cannot prohibit instruction rearrangement and processor optimization.

[But pay special attention to the use of volatile double check locks in previous versions of Java 5 is still problematic. The reason is that the JMM (Java Memory Model) before Java 5 is flawed. Even declaring variables as volatile cannot completely avoid reordering. The main reason is that the code before and after volatile variables still has reordering problems. This volatile shielding reordering problem was fixed in Java 5, so after that, you can use volatile with confidence. ]

public class LazySingleton {
    private volatile static LazySingleton singleton = null;

    private LazySingleton() {
    }

    public static LazySingleton getSingleton() {
        if (singleton == null) {
            synchronized (LazySingleton.class) {
                if (singleton == null)
                    singleton = new LazySingleton();
            }
        }

        return singleton;
    }
}

However, it is actually possible to create multiple instances through reflection:

public class SingletonTest {

    public static void main(String[] args) throws Exception{

        Class clz = Class.forName("LazySingleton");
        // "true" indicates that the reflected object should suppress(废弃) Java language access checking when it is used
        Constructor constructor = clz.getDeclaredConstructor();
        constructor.setAccessible(true);
        Object obj1 = constructor.newInstance();
        Object obj2 = constructor.newInstance();
    }
}

Four, [Lazy man mode] Multi-threaded safe singleton mode example four (using the static inner class "Effective Java" recommended)

When the external class is loaded, the internal class does not need to load the internal class immediately, and the internal class does not need to be initialized if the internal class is not loaded. Therefore, the singleton object does not occupy memory after the external class is loaded.

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

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

Five, singleton mode instance five (not actually used, to be verified)

We can access the instance through EasySingleton.INSTANCE, which is much simpler than calling the getInstance() method. Enumeration creation is thread-safe by default, so there is no need to worry about double checked locking, and it can also prevent deserialization from causing new objects to be recreated. But it is still rare to see someone write this way, probably because it is not familiar.

public enum EasySingleton{
    INSTANCE;
}

 

Guess you like

Origin blog.csdn.net/sjmz30071360/article/details/89357233