1. Basic Concepts

1. Basic concepts of singletons

Definition:

  • Ensure that a class has one and only one instance object

Advantages:

  • Having only one instance in memory reduces memory usage, especially in scenarios where objects are frequently created and destroyed. The singleton mode is advantageous.
  • Avoid multiple resource occupation, such as read configuration, write files, and other operations. Only one object can operate resources, avoiding multiple memory objects to operate resources simultaneously.
  • Singletons can set global access points and share resources.

Disadvantages:

  • Scaling is difficult, and singletons generally have no interface
  • The singleton pattern conflicts with the singleton responsibility principle, which puts multiple business logic in a single class.
  • Singleton objects that hold Context are prone to memory leaks and are best passed ApplicationContext

Usage Scenarios:

  • You need a shared access point or shared data in your project, such as global configuration files, database management classes, network request management classes, and so on.
  • Creating objects consumes a lot of resources, for example, accessing I/OS and databases.

Key points:

  • Constructors are not developed externally
  • Ensure that there is only one object in a multithreaded environment

2. Non-thread-safe singleton:

public class Singleton {
	private static Singleton singleton = null;
	
	private Singleton(){

	}

	public static Singleton getInstance() {if(singleton == null) {
			singleton = new Singleton();
		}
		returnsingleton; }}Copy the code

If two threads execute singleton == null at the same time and both threads meet the conditions, two objects will be created, which violates the singleton principle of creating only one object.

Thread-safe singleton

1. Hungry

public class Singleton {
    private static Singleton singleton = new Singleton();

    private Singleton() {

    }

    public static Singleton getInstance() {
        returnsingleton; }}Copy the code
  • The Singleton is initialized static when the class is loaded. There is only one static variable in memory, so there is no thread-safe problem.
  • Disadvantages: Creating the object first without actually calling it causes unnecessary memory consumption.

2, lazy (synchronous method)

public class Singleton {
    private static Singleton singleton = null;
    private Singleton(){

    }
    public synchronized static Singleton getInstance() {if(singleton == null) {
            singleton = new Singleton();
        }
        returnsingleton; }}Copy the code
  • Implementation principle: getInstance is modified with synchronized keyword to ensure that only one thread executes the creation logic in getInstance method and only one object can be created.
  • Advantages: Create only when needed, reducing unnecessary memory consumption.
  • Disadvantages: Synchronized keyword has efficiency problem. When thread A executes getInstance method, thread B can only wait. Thread B can continue to execute only after thread A finishes executing getInstance method.

3, lazy (double check lock)

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

    private Singleton() {

    }

    public static Singleton getInstance() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if(singleton == null) { singleton = new Singleton(); }}}returnsingleton; }}Copy the code
(1) The role of double-layer judgment
  • If the outer singleton == null is true, the internal logic modified by synchronized will be executed; otherwise, the singleton object will be acquired directly, ensuring that there is no need to wait for object creation.
  • The inner singleton == null judgment is used to check again if the object is created. Why: When thread A executes synchronized modified code, thread B is executing internal logic, so thread A can only wait. Thread B completes and creates the object, while thread A doesn’t know it will create it again, so it needs to check again if the object is empty.
(2) The role of volatile

Singleton = new Singleton() = new singleton () = new singleton () = new singleton () = new singleton () The Java compiler allows instructions to be executed out of order, so the sequence of steps 2 and 3 is not guaranteed. It is possible that thread B executes the code and the singleton points to the memory space. But the member variable has not been initialized. If thread A finds that the object is not empty by singleton== null, it will use the object, but the member variable is not initialized.

Singleton = new Singleton(); volatile

  • Implementation principle: Implement thread safety through synchronized and volatile.
  • Advantages: Reduces the scope of modification by synchronized keyword
  • Disadvantages: Volatile blocks necessary code optimizations for the compiler, so it can be inefficient

4. Static inner classes

public class Singleton {
    private Singleton() {

    }

    private static class SingletonInner {
        private static final Singleton sInstance = new Singleton();
    }

    public static Singleton getInstance() {
        returnSingletonInner.sInstance; }}Copy the code
  • Implementation principle: (1) how to ensure thread safety: the use of the Classloader mechanism to ensure that there is only one thread during initialization. The virtual machine ensures that static class initialization methods are locked and synchronized correctly in A multithreaded environment. If multiple threads are initialized, only one thread (thread A) will perform the initialization and the other threads (thread B) will have to block and wait. After thread A finishes executing, thread B wakes up and does not enter the initialization method. The same classloader will only be initialized once. (2) Whether it is lazy loading: The inner class is only loaded when it is called. Although the Singleton is loaded, the inner class does not need to be loaded immediately, so the Singleton has not been instantiated yet, which is lazy (lazy loading). It will only be instantiated when the getInstance method is actively called.
  • Advantages: Lazy loading, and no thread synchronization problems, more efficient.

5, the enumeration

public enum Singleton{
    SINGLETON;
}
Copy the code
  • How it works: After decompilating the enumeration, the SINGLETON is declared static. The virtual machine guarantees thread-safety for a static variable, so the enumeration is thread-safe.
  • Advantages: Effective Java notes that it is fully functional, simple to use, provides a serialization mechanism for free, and absolutely prevents multiple instantiations in the face of complex serialization or reflection attacks. Other singletons that prohibit recreating objects during deserialization need to be implemented in the readResolve method. Return sInstance.
private Object readResolve() throws ObjectStreamException {
    return sInstance;
}
Copy the code

References:

  • Zen of Design Patterns
  • Android source code design pattern analysis and combat