Q: How many ways are there to implement the singleton pattern? What is the purpose of double locking in the slacker style? What is the purpose of two short calls?

Lazy (thread unsafe)

The ultimate goal of the singleton pattern is simply to get the existing instance object, instantiate one if there is none, and return one if there is one. Lazy, as the name suggests, is when I don’t instantiate an instance until you first use it, and then instantiate it when I have to.

When it comes to singletons, most developers will simply write the following code:

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

The above code is lazy singleton, instantiating instance only when the getInstance method is first called.

This singleton pattern is not a problem in single-threaded cases, but it is important to note that in multi-threaded cases, this singleton pattern can be instantiated multiple instances, that is, this lazy mode is not thread safe, so it is not recommended in general.

Lazy (thread-safe)

As with most thread-unsafe problems, a lazy thread-safe singleton can be locked, such as the static getInstance method used to get the singleton in the example above:

public static synchronized SingleTon getInstance(){
        if (instance==null){
            instance = new SingleTon();
        }
        return instance;
    }
Copy the code

Obviously, this method can solve the problem of unsafe threads, even if there is only one singleton in multi-thread parallelism, but the disadvantage of this method is that it is too inefficient. If you think about it carefully, it is necessary to lock only when instance is instantiated for the first time, and after instance is instantiated, No matter which thread reads the instance, it is not null and can directly return the current instance. The operation of locking the whole method will lock every singleton, which is very inefficient.

Double check lock

In view of the above problems, a third singleton pattern is written, that is, the singleton pattern of double-checked locks:

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

So-called double check the lock, that is, as shown in the example above, for instance objects twice before, during, and after locking sentenced to empty inspection, lock is the first time for thread synchronization object instantiation, and inside the lock have the second sentence empty because there may be multiple threads to enter inside the first layer if judgment, and wait in line outside the locking code block, if not for the second time inspection within the lock, You can still instantiate multiple objects.

And after double check, in addition to instantiate the need for locking synchronization for the first time, after the thread with the first layer of the if judgment is not null can be returned directly, instead of every access to the singleton lock synchronization, so compared with the front two singleton pattern of writing, more recommend use this approach to double check the lock to write a singleton.

However, the double-checked lock is not perfect, and the problem arises when instantiating a SingleTon. The code instance = new SingleTon() does three things:

  1. Allocate memory for instance
  2. Initialize a member variable by calling the SingleTon constructor
  3. The instance reference points to the previously allocated memory space

The JVM can’t actually guarantee the order in which 2 and 3 of these things are executed, meaning that 123 or 132 is the order in which they can be executed, and 123 is the order in which they can be executed, while 132 is the order in which instance is a reference. If another thread calls getInstance, it will return the instance that has not been initialized to the member variable, and the program may crash.

To solve this problem, we added the Violate keyword:

private static violate SingleTon instance;
Copy the code

Violate can be interpreted as a read/write lock. Once instance is operated after violate modification is used, the instance can only be read after all the three steps mentioned above are completed. Only with violate keyword can the problems mentioned above be solved.

The hungry type

As you can see, all the problems mentioned above are rooted in multi-threading. If you can solve the problem of multi-threading synchronization, then the previous problems will be solved easily, so there is a hungry singleton pattern.

Lazy: lazy: lazy: lazy: lazy: lazy: lazy: lazy: lazy: lazy: lazy: lazy: lazy: lazy: lazy: lazy: lazy: lazy: lazy: lazy: lazy: lazy: lazy: lazy: lazy: lazy: lazy: lazy: lazy: lazy: lazy: lazy: lazy

public class SingleTon { private static SingleTon instance = new SingleTon(); private SingleTon(){ } public static SingleTon getInstance(){ return instance; }}Copy the code

Obviously, since instance is decorated as static, it is instantiated the first time the class is loaded into memory. Because of the nature of the class loading mechanism, this method is inherently thread-safe, so it does not have the problems of the previous three methods.

Because of the static modifier, instance is initialized when the class is first loaded into memory. If the singleton is not used during a run, resources are wasted to load the object and delay loading is not possible. On the other hand, sometimes we need to rely on resources created by other classes to create the singleton, i.e. we need to actually create the singleton when we call getInstance for the first time.

Static inner class

For the problem of hanhanian method above, can there be a singleton mode writing method that can not only delay loading, but also solve the synchronization problem? This requires a static inner class.

public class SingleTon { private static class SingleTonInstance{ private static SingleTon instance = new SingleTon(); } private SingleTon(){ } public static SingleTon getInstance(){ return SingleTonInstance.instance; }}Copy the code

Since a static inner class is private, there is no other way to access it than the getInstance method, so singletons can actually be created only when getInstance is called, implementing the singleton creation delay, and there are no thread synchronization problems due to the fact that static classes are only loaded once

  • Would require additional work to serialize, otherwise a new instance would be created each time a serialized object was deserialized;
  • You can still use reflection to force calls to private constructors to build singletons.

The enumeration

In fact, the most elegant singleton in Java is to use enumerations.

public enum  EnumSingle {
    INSTANCE;
}
Copy the code

Enumerations, on the other hand, provide an automatic serialization mechanism that prevents objects from being recreated during deserialization, making them the most recommended singleton for the new effective Java.

Pay attention to

Enumerations are the most recommended singletons. In theory, enumerations that address synchronization, reflection call constructors, and deserialization are the most elegant singletons. However, enumerations are not ordinary classes. Using enumerations as singletons naturally removes some of the features of ordinary classes (inheritance, etc.), and enumerations, like hanhandedly written, do not allow lazy loading.

So to sum up, enumeration is preferred for singletons if lazy loading is not required; Static inner classes are preferred if lazy loading is required.

Of course, the above analysis is specific to Java, but enumerations are actually not recommended in Android due to performance issues, and most Of the Java used in Android is above version 1.5, so static inner classes or double check locks are recommended for Android singletons.