Simple definition

  • For the singleton pattern, you are already familiar with the development students can not be familiar with it, is also widely used in various projects, no matter what high-level programming language is used, design pattern is always accompanied by it.
  • Simply put, the singleton pattern ensures that there is only one instantiated object in a class, and then provides a global access point

Advantages and disadvantages of the singleton pattern

  1. Main advantages:
  • Provides controlled access to a unique instance.
  • Since only one object exists in system memory, system resources can be saved, and the singleton pattern can undoubtedly improve system performance for some objects that need to be created and destroyed frequently.
  • A variable number of instances is allowed.
  1. Main disadvantages:
  • Because there is no abstraction layer in the simple interest pattern, it is very difficult to extend singleton classes.
  • Singleton classes are too heavy on responsibilities and violate the “single responsibility principle” to some extent.
  • Abuse of singletons will bring some negative problems, for example, in order to save resources, the database connection pool object is designed as a singleton class, may lead to too many programs sharing the connection pool object and the connection pool overflow; If an instantiated object is not used for a long time, the system considers it garbage and collects it, resulting in a loss of object state.

Talk about how Java and Kotlin are implemented

Several singleton patterns in Kotlin

Hangry singleton

  • Hanchian singleton pattern is a simple way to implement singleton pattern. It has a feature that the singleton object will be instantiated whether it is needed or not.

  • Kotlin is very simple to implement, just define an object expression, no need to manually set the constructor private and provide global access points, which kotlin compiler does for you

object KSingleton : Serializable {// Implement the Serializable interface, which controls deserialization through the private, instantiated readResolve method
    fun doSomething(a) {
        println("do some thing")}private fun readResolve(a): Any {// Prevent the singleton from regenerating the object during deserialization
        return KSingleton// Since the readResolve hook method is called when deserializing, you only need to return the current KSingleton object instead of creating a new one}}Copy the code

Thread-safe lazy singleton

  • The singleton instance is created when the class is loaded, and initialized when we use it
class KLazilySingleton private constructor() : Serializable {
    fun doSomething(a) {
        println("do some thing")
    }
    companion object {
        private var mInstance: KLazilySingleton? = null
            get(a) {
                returnfield ? : KLazilySingleton() }@JvmStatic
        @Synchronized// Add a synchronized lock
        fun getInstance(a): KLazilySingleton {
            return requireNotNull(mInstance)
        }
    }
    // Prevent the singleton from regenerating the object during deserialization
    private fun readResolve(a): Any {
        return KLazilySingleton.getInstance()
    }
}
Copy the code

Double Check Lock (DCL) modified lazy singleton

  • The thread-safe singleton mode uses a synchronized lock directly to lock the getInstance method, which must be acquired each time the method is called. However, if the singleton is already initialized, there is no need to acquire the lock and simply return the singleton instance. Hence the DCL implementation singleton approach

  • In Kotlin, the singleton for thread-safe DCL is very, very simple, with only three lines of code, called the Companion Object + lazy proxy

class KLazilyDCLSingleton private constructor() : Serializable {//private constructor(

    fun doSomething(a) {
        println("do some thing")}private fun readResolve(a): Any {// Prevent the singleton from regenerating the object during deserialization
        return instance
    }

    companion object {
        // Using the @jvmStatic annotation, instance is called in Java as if it were a static function,
        / / similar KLazilyDCLSingleton. GetInstance (), if do not add annotations, in Java must call like this: KLazilyDCLSingleton.Com panion. GetInstance ().
        @JvmStatic
        // Use the lazy proxy and set LazyThreadSafetyMode to SYNCHRONIZED to ensure thread safety
        val instance: KLazilyDCLSingleton by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { KLazilyDCLSingleton() }
    }
}
Copy the code

Static inner class singleton

  • Although DCL can solve problems such as resource consumption, redundant synchronized synchronization and thread safety to a certain extent, DCL failure still exists in some cases
  • The singleton pattern of DCL is generally not recommended in multithreaded environments. So the static inner class singleton implementation is introduced
class KOptimizeSingleton private constructor() :Serializable {//private constructor(
    companion object {
        @JvmStatic
        fun getInstance(a): KOptimizeSingleton {// Global access point
            return SingletonHolder.mInstance
        }
    }

    fun doSomething(a) {
        println("do some thing")}private object SingletonHolder {// Static inner class
        val mInstance: KOptimizeSingleton = KOptimizeSingleton()
    }

    private fun readResolve(a): Any {// Prevent the singleton from regenerating the object during deserialization
        return SingletonHolder.mInstance
    }
}
Copy the code

Enumerated the singleton

  • Enumeration singletons are implemented to prevent deserialization, because we all know that enumeration class deserialization does not create new object instances
  • The serialization mechanism for enumeration types guarantees that only existing instances of enumeration types are found, not new instances are created
enum class KEnumSingleton {
    INSTANCE;

    fun doSomeThing(a) {
        println("do some thing")}}Copy the code

Several singleton patterns in Java

Lazy (thread unsafe)

// lazy singleton class. Instantiate yourself on the first call
public class Singleton {  
    // Private constructor
    private Singleton(a) {} 
    // Private static variable
    private static Singleton single=null;  
    // Exposed public static methods
    public static Singleton getInstance(a) {  
         if (single == null) {    
             single = new Singleton();  
         }    
        returnsingle; }}Copy the code
  • Generally speaking, the lazy style is divided into three parts: private constructors, private global static variables, and public static methods
  • Play an important role in the static modifier is the static keyword, we know that in the program, any variables or code is at compile time by the system automatically allocates memory to store, and the so-called static is to point to in the compiled allocated memory will always exist, until the program exits memory will release the space, thus ensure the singleton class instance once created, It will not be reclaimed by the system unless it is manually set to NULL.
  • The downside of this approach is thread-safety, which is solved by using the synchronized keyword, a second way of writing singleton.

Lazy (thread-safe)

public class Singleton {  
    // Private static variable
    private static Singleton instance;  
    // Private constructor
    private Singleton (a){};// Public synchronized static methods
    public static synchronized Singleton getInstance(a) {  
    if (instance == null) {  
        instance = new Singleton();  
    }  
    returninstance; }}Copy the code
  • This singleton implementation adds the synchronized keyword to the getInstance () method, which tells the Java (JVM) that getInstance is a synchronized method.
  • Synchronization means that when two concurrent threads access the synchronized method in the same class, only one thread can be executed at a time, and the other thread must wait for the current thread to complete. Therefore, synchronization makes thread-safe, ensuring that there is only one instance of a singleton.
  • The downside is that it synchronizes every time getInstance () is called, incurring unnecessary synchronization overhead. This mode is generally not recommended.

Hungry (thread-safe)

// The hunchman singleton class. When the class is initialized, it is instantiated by itself
public class Singleton {  
    // Static modified static variables are permanent once created in memory
    private static Singleton instance = new Singleton();  
    private Singleton (a){}  
    public static Singleton getInstance(a) {  
    returninstance; }}Copy the code
  • Hungry type has been created while class to create a static object for the use of system, won’t change, so naturally is thread-safe. Instance =new Singleton() can be written as:
 static {  
    instance = new Singleton();  
    }  
Copy the code
  • It is also based on the classloder mechanism to avoid multithreading synchronization problems. Instance is instantiated when the class is loaded.

DCL double check mode

public class Singleton {  
    private static Singleton singleton;  // Static variables
    private Singleton (a){}  // Private constructor
    public static Singleton getInstance(a) {  
      if (singleton == null) {  // Level 1 check
          synchronized (Singleton.class) {  
          if (singleton == null) {  // Layer 2 check
              singleton = newSingleton(); }}}returnsingleton; }}Copy the code
  • What makes this pattern special is the getInstance () method, where the Singleton is checked for empty twice, the first to avoid unnecessary synchronization and the second to create the instance in case of NULL.

Singleton = new singleton (); Statement, which looks like a line of code, but is not an atomic operation. This code will eventually be compiled into multiple assembly instructions, which do roughly three things:

  1. Allocate memory to the instance of Singleton
  2. Call the constructor of Singleton () to initialize the member field
  3. Point the Singleton object to the allocated memory space (i.e. the Singleton is not empty)

However, after JDK1.5, the volatile keyword was officially introduced and the singleton code was defined to address DCL failures.

private volatile static Singleton singleton;  // Use volatile
Copy the code

Static inner class singleton pattern

public class Singleton {  
    private Singleton (a){};// Private constructor
    public static final Singleton getInstance(a) {  
        return SingletonHolder.INSTANCE;  
    }  
    // Static inner class defined
    private static class SingletonHolder {  
        private static final Singleton INSTANCE = new Singleton();  // Where the instance is created}}Copy the code
  • INSTANCE is not initialized when the Singleton class is loaded for the first time. INSTANCE is initialized only when the Singleton getInstance () method is called for the first time.
  • Therefore, the first call to the getInstance () method causes the virtual machine to load the SingletonHolder class, which not only ensures the uniqueness of the singleton, but also delays the instantiation of the singleton.

Enumerated the singleton

The previous singleton implementations tend to be a little more cumbersome, or in some specific cases, something bad happens. The following is an implementation of the enumeration singleton pattern:

public enum Singleton {  / / enum enumeration class
    INSTANCE;  
    public void whateverMethod(a) {}}Copy the code

The biggest advantage of the enumeration singleton pattern is that it is simple to write. Enumerations in Java are the same as normal classes. Enumerations can have not only fields, but also methods. Enumeration singletons do not regenerate new instances even during deserialization. For the other methods, the following methods must be added:

private Object readResolve(a)  throws ObjectStreamException{
    return INSTANCE;
}
Copy the code

This ensures that no new methods are generated during deserialization

Use containers to implement the singleton pattern

public class SingletonManager {private static Map<String, Object> objMap = new HashMap<String,Object>();// Use HashMap as the cache container
  private Singleton(a) {}public static void registerService(String key, Objectinstance) {if(! Objmap.containskey (key) {objmap.put (key, instance);// The first time is saved to Map}}public static ObjectgetService(String key) {return objMap.get(key) ;// Return the object corresponding to key}}Copy the code
  • At the beginning of the program, multiple singletons are injected into a unified management class, and objects of the corresponding type are obtained according to the key when used.

Application scenarios

  1. So when should you consider using the singleton pattern?
  • The system only needs one instance object, if the system requires a unique sequence number generator or resource manager, or if the resource consumption is too high to allow the creation of only one object.
  • A single instance of a customer invoking class is allowed to use only one public access point, and the instance cannot be accessed by any other means than that public access point.
  1. Below we combine some Android source code to analyze the next

The EventBus framework commonly used in Android

  • Can we see how singletons are used in EventBus, mainly with double-checked DCL
static volatile EventBus defaultInstance;
    public static EventBus getDefault(a) {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = newEventBus(); }}}return defaultInstance;
    }
Copy the code

This way it is very resource efficient, and the first time it is executed, the singleton is instantiated, but the first time it is loaded it is slower and acceptable

Singleton pattern implementation of LayouInflater

  • Basic usage
LayoutInflater mInflater = LayoutInflater.from(this);
Copy the code

Get an example of LayoutInflater from LayoutInflater.

  1. Get the LayoutInflater service from LayoutInflater. From (context)
/** * Obtains the LayoutInflater from the given context. */
public static LayoutInflater from(Context context) {
    LayoutInflater LayoutInflater =
            (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    if (LayoutInflater == null) {
        throw new AssertionError("LayoutInflater not found.");
    }
    return LayoutInflater;
}
Copy the code
  1. To see how context.getSystemService works, the implementation of context is ContextImpl
@Override
public Object getSystemService(String name) {
    return SystemServiceRegistry.getSystemService(this, name);
}
Copy the code
  1. Enter the SystemServiceRegistry class
/** * Gets a system service from a given context. */
public static Object getSystemService(ContextImpl ctx, String name) { ServiceFetcher<? > fetcher = SYSTEM_SERVICE_FETCHERS.get(name);returnfetcher ! =null ? fetcher.getService(ctx) : null;
}
Copy the code
  • At this point, I’m sure you already feel that this is the implementation of the singleton pattern using containers, by contrast, it is
private static finalHashMap<String, ServiceFetcher<? >> SYSTEM_SERVICE_FETCHERS =newHashMap<String, ServiceFetcher<? > > ();Copy the code
  • Use map to store system services as key-value pairs. Injected when registerService is called.
/** * Statically registers a system service with the context. * This method must be called during static initialization only. */
private static <T> void registerService(String serviceName, Class
       
         serviceClass, ServiceFetcher
        
          serviceFetcher)
        
        {
    SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
    SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}
Copy the code
  1. We can also look at when these services are registered
static {

registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
        new CachedServiceFetcher<LayoutInflater>() {
    @Override
    public LayoutInflater createService(ContextImpl ctx) {
        return new PhoneLayoutInflater(ctx.getOuterContext());
    }});
}
Copy the code
  • Obviously, this is done in a static block of code to register the service, the first time the class is loaded, and only once to keep the instance unique
  • From this process, it can be seen that the system stores the service in the form of key-value pairs in the HashMap. Users only need to obtain the specific service object when using it. When obtaining the specific object for the first time, getSystemService is called to obtain the specific object. RegisterService is called to cache the object in a list through the map, which can be fetched directly from the container the next time. Avoid creating objects repeatedly to achieve the singleton effect. Reduced resource consumption.
  • In the Android source code, when the APP starts, the virtual machine first loads the class and registers various ServiceFetchers, such as LayoutInflater services. These services are stored as key-value pairs in a HashMap. Users only need to obtain the corresponding ServiceFetcher based on the key, and then obtain the specific service object through the getService function of the ServiceFetcher object. The creatService function of ServiceFetcher is called to create the service object for the first time, and the service object is cached in a list. The next time the service object is fetched from the cache, avoiding repeated object creation. The system core services in Android exist in the form of singletons, reducing resource consumption.

ImageLoader image loading framework

  • The image loading framework ImageLoader instance is created using the singleton pattern. Because the ImageLoader contains thread pools, caching systems, and network requests, it is very resource-consuming and should not be used to create multiple objects
ImageLoader.getInstance();// Create a global instance in your Application.//getInstance() executes the source code
 public static ImageLoader getInstance(a) {
        if(instance == null) {// Double check DCL singleton mode
            Class var0 = ImageLoader.class;
            synchronized(ImageLoader.class) {// Synchronize code blocks
                if(instance == null) {
                    instance = new ImageLoader();// Create a new instance}}}return instance;// Return an instance
    }
Copy the code

Therefore, we can consider the singleton pattern if creating an object consumes too many resources

conclusion

The above are the analysis and summary of the singleton mode sorted out by my classmates in combination with the online materials. To put it simply, singleton mode is very common in the design mode. It is simple and simple at the same time, which is worth further discussion

  • One core principle is private construction, and fetching an instance through static methods.
  • In this process, thread safety must be guaranteed.
  • Static internal implementation singletons or double-checking singletons with the Volatile keyword are recommended