English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Singleton Pattern in Java Design Patterns

java Design Pattern - Singleton Pattern

Preface:

During the software development process, there are often some objects that we only need one of, such as: thread pool (threadpool), cache (cache), dialog box, preference settings, and so on. If multiple instances of these objects are created, it may lead to some unnecessary troubles, such as: abnormal program behavior, excessive resource usage, and so on. At this point, the Singleton pattern can ensure that only one instance of a class exists, and provide a global access point. Below, we will discuss what methods should be used to implement the Singleton pattern from a simple Singleton class.

/**
 * The most classic singleton class
 */
public class Singleton {
  // Set as a static variable to record the unique instance of Singleton
  private static Singleton singleInstance;
  private Singleton() {
    // The constructor is declared as private, so it can only be called within the Singleton class
  }
  /*
   * Get the Singleton object, if it has not been instantiated, instantiate an object and return this instance
   */
  public static Singleton getInstance() {
    if (singleInstance == null) {
      singleInstance = new Singleton();
    }
    return singleInstance;
  }
  // Other methods
}

From the above example, it can be seen that the Singleton class manages the instantiation process of this class itself and provides a global access point, which is set to the static getInstance() method. When other classes need to use Singleton, it will return an instance. This kind of singleton pattern has the advantage of deferred instantiation. In simple terms, deferred instantiation is to delay initialization, creating the instance only when the class is needed, rather than creating an instance when the class is loaded. This has the advantage of avoiding waste of performance. For example, some objects may not be used at the beginning of the program, or they have never been used during the execution of the program. However, this example also has a disadvantage, that is, it is not thread-safe. Because if multiple threads execute the getInstance() method at the same time, and Singleton has not yet created a new Singleton() instance, then all threads will consider singleInstance to be null and will instantiate Singleton. In this case, multiple Singleton instances will be created, which is obviously not in line with the original intention of the singleton pattern. So the next step may be to improve it

public class SingletonA {
  private static SingletonA singletonA;
  private SingletonA() {
  }
  /*
   * Add the synchronized keyword to make the getSingletonA method a synchronized method
   */
  public static synchronized SingletonA getInstanceA() {
    if (singletongA == null) {
      singletongA = new SingletonA();
    }
    return singletongA;
  }
  // Other methods
}

From this example, adding synchronized can make getInstanceA() a synchronized method. At this point, the thread needs to wait for other threads to leave the method before it can enter, which makes the method only be executed by one thread at a time.

Maybe the problem is solved, but we should know that synchronized methods can affect the program's execution efficiency. In this example, we just want to solve the problem that the first time the getInstance() method is executed in the first example will not produce multiple instances, but in this example, it will cause the synchronized method getInstanceA() to be called every time an instance is needed, and the call to synchronized will be redundant after there is already an instance because we no longer need to worry about the singleton class being created again. Therefore, we still need to make some improvements.

Since we mentioned lazy instantiation above, if we don't need it, it's much simpler.

public class SingletonB {
  // Create the singleton in the static initializer (static initializen) to ensure thread safety
  private static SingletonB singletonB = new SingletonB();
  private SingletonB() {
    // Constructor
  }
  public static SingletonB getInstaceB() {
    // It has been instantiated, use it directly
    return singletonB;
  }
}

The above approach is to create an instance immediately when the JVM loads this class, because the JVM will create the instance before the thread accesses it, so it is thread-safe. However, this may result in resource waste compared to lazy instantiation. Moreover, if the class is large, it may prolong the program initialization time.

So, can we use lazy instantiation without causing thread unsafe and increase access efficiency? Let's improve it with double-checked locking next.

/**
 * Double-checked locking Singleton pattern
 */
public class SingletonC {
  private volatile static SingletonC singletonC;
  private SingletonC() {
  }
  public static SingletonC getInstanceC() {
    if (singletonC == null) {
      synchronized (SingletonC.class) {
        if (singletonC == null) {
          singletonC = new SingletonC();
        }
      }
    }
    return singletonC;
  }
}

The above example first checks the instance, if it does not exist, it enters the synchronized block, and enters the synchronized block again to check. If it is still null, the instance will be created. Therefore, singletonC = new SingletonC() will only be executed once, and after calling getInstanceC(), it will return directly because there is an instance, so except for the first call, it will not go through the synchronized method as in the second example. This can reduce the execution time of getInstanceC(). It is believed that there is a volatile keyword here, which makes singletonC visible to all threads after initialization, and multiple threads can correctly handle this SingletonC variable. However, it should be noted that the volatile keyword can only be used in Java 5and subsequent use, if before this version will cause this double check to fail.

When using the Singleton pattern, if there are multiple class loaders (classloader), it is necessary to specify the class loader by yourself and specify using one class loader. Because each class loader defines a namespace, different class loaders may load the same class, resulting in multiple instances of the singleton class being created.

Thank you for reading and hope it can help everyone. Thank you for your support of this site!

Statement: The content of this article is from the network, the copyright belongs to the original author. The content is contributed and uploaded by Internet users spontaneously. This website does not own the copyright, does not undergo manual editing, and does not assume relevant legal liabilities. If you find any content suspected of copyright infringement, please send an email to: notice#oldtoolbag.com (Please replace # with @ when sending an email for reporting. Provide relevant evidence, and once verified, this site will immediately delete the infringing content.)

You May Also Like