Application scenario: Stateless tool classes. Many tool classes require only one instance to save memory. But we write very much, the correct writing method is rarely seen, I hope after reading this article can help you sort out. Hungry: Create the singleton from the beginning, whether you need it or not (like a hungry person) Lazy: Create the singleton when you really need it (like a lazy person)

1. (static constant)

*/ Public class Singleton1 {private final static Singleton1 INSTANCE = new Singleton1(); private Singleton1() { } public static Singleton1 getInstance() { return INSTANCE; }}Copy the code

Advantages: This is a simpler way to write, which is to complete the instantiation when the class is loaded. Thread synchronization issues are avoided. Disadvantages: Instantiation is done when the class is loaded, not Lazy Loading. If the instance is never used, then memory is wasted.

2. Hungry (static code block)

Public class Singleton2 {private final static Singleton2 INSTANCE; static { INSTANCE = new Singleton2(); } private Singleton2() { } public static Singleton2 getInstance() { return INSTANCE; }}Copy the code

This method is similar to the above method, except that the process of class instantiation is placed in the static code block, and the code in the static code block is executed when the class is loaded, and the class instance is initialized. The pros and cons are the same as above.

3. Lazy (thread unsafe)

Public class Singleton3 {private static Singleton3 instance; private Singleton3() { } public static Singleton3 getInstance() { if (instance == null) { instance = new Singleton3(); } return instance; }}Copy the code

This provides a Lazy Loading effect, but can only be used in a single thread. If, in multithreading, one thread enters an if (singleton == NULL) statement block and another thread passes the statement before it can proceed, instances are created multiple times. Therefore, this approach cannot be used in multithreaded environments.

4. Lazy (thread-safe, synchronous approach)

*/ public class Singleton4 {private static Singleton4 instance; private Singleton4() { } public synchronized static Singleton4 getInstance() { if (instance == null) { instance = new Singleton4(); } return instance; }}Copy the code

To solve the thread-unsafe problem of the third implementation above, we simply do a thread synchronization, which we do with the getInstance() method. Disadvantages: Inefficient, every thread that wants to get an instance of a class needs to execute the getInstance() method in synchronization. If you want to get an instance of the class, return it. Method synchronization efficiency is too low to improve.

5. Lazy (thread-safe, synchronized code blocks)

*/ public class Singleton5 {private static Singleton5 instance; private Singleton5() { } public static Singleton5 getInstance() { if (instance == null) { synchronized (Singleton5.class) { instance = new Singleton5(); } } return instance; }}Copy the code

Because the fourth implementation method is too inefficient to synchronize, the synchronization method is discarded and instantiated code blocks are generated synchronously instead. But this synchronization does not function as thread synchronization. As with the third implementation, if one thread enters the if (Singleton == NULL) statement block and another thread passes the statement before it can proceed, multiple instances are created.

6. Double check

*/ public class Singleton6 {private volatile static Singleton6 instance; private Singleton6() { } public static Singleton6 getInstance() { if (instance == null) { synchronized (Singleton6.class) { if (instance == null) { instance = new Singleton6(); } } } return instance; }}Copy the code

The concept of double-check is familiar to multithreaded developers, and as shown in the code, we perform two if (Singleton == null) checks to ensure thread-safety. If (singleton == null), return to instantiate the object. Synchronized (this) cannot be synchronized here, because getInstance is a static method, and an unstatic or uninstantiated class object cannot be used within it. Advantages: Thread-safe; Lazy loading; High efficiency.

6.1 为什么要double-check

6.1.1 Reasons for the need for a second

Instance == null (singleton== null); instance == null (singleton== null) So one thread enters the lock statement and enters the second singleton == NULL, while another thread waits outside the lock statement. When the first thread finishes executing the new Singleton () statement, it exits the lock area, and the second thread can enter the lock block. If there is no second Singleton == null, the second thread can call the new Singleton () statement. The second thread will also create a Singleton instance, which again defeats the purpose of the Singleton pattern, so double-checked locking is required.

6.1.2 Reasons for needing the first weight

If the first singleton == null is removed, the program can still run safely in multiple threads. Consider the case where the first singleton == null: When two threads arrive at the same time, it is assumed that the first thread will enter the lock block and execute new Singleton () successfully. When the first thread exits the lock block, the Singleton static variable is no longer null. So when the second thread enters the lock, it is blocked by the second singleton == null, and new Singleton () cannot be executed, so it is possible to implement singleton mode without the first singleton == NULL. Why do I need the first singleton == null? There is a performance issue here, because for singletons, new Singleton () only needs to be executed once, whereas without the first Singleton == null, every time a thread enters getInstance (), If I had added the first singleton == null, then I would have locked the first time to synchronize the thread. After that, I would have simply returned the singleton instance. There is no need to enter the lock block at all, thus solving the performance problems caused by thread synchronization.

6.1.3 Why Volatile

In particular, instance = new Singleton(), which is not an atomic operation, actually does three things in the JVM: 1. Allocate memory for instance 2. Call the constructor of the Singleton to initialize member variable 3. The instance object is referred to the allocated memory space (the instance is not null after this step) but there is an optimization for instruction reordering in the JVM’s just-in-time compiler. That is to say, the order of step 2 and step 3 above is not guaranteed, and the final execution order may be 1-2-3 or 1-3-2. If instance 1 is suspended by the scheduler before instance 2 is executed and instance 2 is checked by the scheduler for the first time, instance 2 is already non-null (but not initialized), and instance 1 is null/false/0. So thread two will simply return instance, use it, and then either automatically report an error or see an unexpected value (because the value of the property is the default value, not the desired value). However, if thread one has run out of code from a synchronized block, instance must be a correctly constructed instance, as guaranteed by synchronized’s Heppens-before.

7. Static inner class

/**
 * 描述:     静态内部类方式,可用
 */
public class Singleton7 {

    private Singleton7() {
    }

    private static class SingletonInstance {

        private static final Singleton7 INSTANCE = new Singleton7();
    }

    public static Singleton7 getInstance() {
        return SingletonInstance.INSTANCE;
    }
}
Copy the code

This approach is similar to, but different from, the hungrier approach. Both use a classloading mechanism to ensure that only one thread initializes instances. The difference is that the Singleton class is instantiated as soon as it is loaded without lazy-loading, while the static inner class is not instantiated as soon as the Singleton class is loaded. Instead, the getInstance method is called when it needs to be instantiated. The SingletonInstance class is loaded to complete the Singleton instantiation. The static properties of a class are initialized only when the class is first loaded, so the JVM helps us keep our threads safe from entering while the class is initialized. Advantages: Avoid thread insecurity, delay loading, high efficiency.

8.

/** * Description: Singleton */ public enum Singleton8 {INSTANCE; public void whatever() { } }Copy the code

Implement the singleton pattern with the help of enumerations added in JDK1.5. Not only does it avoid multi-threaded synchronization issues and lazy loading, but it also prevents deserialization from recreating new objects.

9. Conclusion

Enumeration is best. The point is made clear in Effective Java: “While the use of enumerations to implement singletons has not yet been widely adopted, enumerations of single-element types have become the best way to implement singletons.”

10. Why is enumeration recommended

10.1 Simple writing

Enumerations are simpler and more elegant than any other way of writing them.

10.2 Thread Security Is guaranteed

An enumeration we define is loaded and initialized by the virtual machine the first time it is actually used, and this initialization is thread-safe. As we know, to solve the concurrency problem of singletons, the main solution is the thread safety problem in the initialization process. So, because of the above features of enumerations, singletons implemented by enumerations are inherently thread-safe.

10.3 Avoid deserialization breaking singletons

Normal Java class deserialization uses reflection to call the class’s default constructor to initialize the object. So, even if the constructor in a singleton is private, it will be destroyed by reflection. Since the deserialized object is new again, this breaks the singleton. However, deserialization of enumerations is not done by reflection. As a result, singleton destruction due to deserialization does not occur.