preface

I first met the design pattern in last winter vacation, when I went over each design pattern, I had a most elementary understanding of the design pattern. This semester, I borrowed several books on design patterns to read and listened to the teacher’s design pattern class, so I have a further understanding of design patterns. You may update your understanding of design patterns from time to time. Each design pattern seems simple, but it’s actually very, very difficult to apply in a complete system. However, I also have very limited resources and not a lot of codes. I can only sum up by reading books, thinking about other people’s coding experience and combining the problems encountered in my own coding process.

How to use it -> how to use it well -> how to use it in conjunction with other patterns is a big gap I think every developer needs to bridge.

1 Introduction to singleton mode

1.1 define

Ensure that a class has only one instance and provide a global access point to access it.

1.2 Why singleton pattern?

There are some objects in our system that we really only need one: thread pools, caches, dialogs, registries, log objects, objects that act as drivers for printers, graphics cards, and other devices. In fact, there can only be one instance of this class of objects, and creating more than one instance can lead to problems such as abnormal program behavior, excessive resource usage, or inconsistent results.

In short, using the singleton pattern provides several benefits:

  • For frequently used objects, you can omit the time it takes to create them, which can be a significant overhead for heavy objects.
  • As the number of new operations is reduced, the system memory is used less frequently, which reduces GC stress and reduces GC pause times.

1.3 Why not use global variables to ensure only one instance of a class?

We know that global variables are divided into static variables and instance variables. Static variables also guarantee that only one instance of the class exists. As soon as the program loads the class’s bytecode without creating any instance objects, static variables are allocated space and can be used.

However, if this object is very expensive and the program is never used in a particular execution, then resources are wasted. Using the singleton pattern, we can create objects only when they are needed, thus avoiding unnecessary waste of resources. Not only because of this reason, we should try to avoid the use of global variables in the program, a large number of global variables to the program debugging, maintenance and other difficulties.

2. Realization of singleton pattern

In general, the singleton pattern is built in two ways in the Java language:

  • Hungry man way. A global singleton instance is built at class load time
  • The lazy way. A global singleton instance is built when it is first used.

Regardless of how they are created, they generally share the following similarities:

  • A singleton class must have a private access-level constructor to ensure that it cannot be instantiated in other code in the system.
  • Instance member variables and uniqueInstance methods must be static.

2.1 Hungry Mode (Thread-safe)

    public class Singleton {
       // Create a singleton instance in a static initializer. This code is thread-safe
        private static Singleton uniqueInstance = new Singleton();
        // The Singleton class has only one constructor and is private, so users cannot create instances of the object through the new method
        private Singleton(a){}
        public static Singleton getInstance(a){
            returnuniqueInstance; }}Copy the code

The “hungry” approach means that the JVM creates a single instance of the class as soon as it is loaded, regardless of whether it is used or not. If it is never used, space is wasted. Typically, space is changed every time the class is called, no judgment is required, saving runtime.

2.2 Lazy (non-thread-safe thread-safe version of synchronized keyword)

public class Singleton {  
      private static Singleton uniqueInstance;  
      private Singleton (a){}// Versions that do not include the synchronized keyword are thread-safe
      public static Singleton getInstance(a) {
          // Check whether the current singleton already exists. If it does, the system returns. If it does not, the system creates a singleton
	      if (uniqueInstance == null) {  
	          uniqueInstance = new Singleton();  
	      }  
	      returnuniqueInstance; }}Copy the code

The “hangovers” approach means that a singleton is built the first time it is used, rather than creating a unique singleton as soon as the JVM loads the class.

However, this approach is obviously thread-unsafe and can cause problems if multiple threads access the getInstance() method at the same time. A common way to ensure thread-safety is to prefix the getInstance() method with the synchronized keyword, as follows:

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

We know that the synchronized keyword is weighted. Although after JavaSE1.6 synchronized keyword mainly includes: biased lock and lightweight lock introduced in order to reduce the performance consumption caused by lock acquisition and lock release and other various optimizations, the execution efficiency has been significantly improved.

However, every time getInstance() is used in a program, it must pass through the layer of synchronized locking, which inevitably increases the time consumption of the getInstance() method and may block. The double-checked locking version we introduce below is designed to solve this problem.

2.3 Lazy (double-checked locked version)

Double-checked locking is used to check whether the instance has been created, and if not, the synchronization is performed. So there’s only one synchronization, which is exactly what we want.

public class Singleton {

    // Volatile ensures that the uniqueInstance variable will be handled correctly by multiple threads when initialized to the Singleton instance
    private volatile static Singleton uniqueInstance;
    private Singleton(a) {}public static Singleton getInstance(a) {
       // Check the instance, if it does not exist, enter the synchronized code block
        if (uniqueInstance == null) {
            // Only the first time to execute this code completely
            synchronized(Singleton.class) {
               // Enter the synchronization block, check again, if it is still null, create the instance
                if (uniqueInstance == null) {
                    uniqueInstance = newSingleton(); }}}returnuniqueInstance; }}Copy the code

Obviously, this approach can significantly reduce the time consumption of getInstance() compared to using the synchronized keyword.

We used the volatile keyword above to ensure data visibility. For more information on volatile, see my article: Java Multithreading learning (iii) : blog.csdn.net/qq_34337272…

Note: Double-checked locked versions do not apply to Java versions 1.4 and earlier. In Java 1.4 and earlier, many JVMS ‘implementation of the volatile keyword invalidates double-checked locking.

2.4 Lazy (Register/Static inner Class)

Static internal implementations of singletons are lazy-loaded and thread-safe.

The SingletonHolder class is explicitly loaded to instantiate instance only when the getInstance method is explicitly called (it is loaded only when an instance of this singleton is used for the first time, and there are no thread-safety issues).

public class Singleton {  
    private static class SingletonHolder {  
    private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (a){}  
    public static final Singleton getInstance(a) {  
    returnSingletonHolder.INSTANCE; }}Copy the code

2.5 Hungry (Enumeration mode)

This implementation is not yet widely adopted, but it is the best way to implement the singleton pattern. It is simpler, automatically supports serialization, and absolutely prevents multiple instantiations (if the singleton class implements the Serializable interface, by default a new instance object is always created for each deserialization. For more information on singleton and serialization, see the article singleton and Serialization.) This approach is recommended by the authors of Effective Java and Java and Patterns.

public enum Singleton {
	 // Define an enumerated element that is an instance of Singleton
    INSTANCE;  
    
    public void doSomeThing(a) {  
	     System.out.println("Enumeration method implementation singleton"); }}Copy the code

Usage:

public class ESTest {

	public static void main(String[] args) {
		Singleton singleton = Singleton.INSTANCE;
		singleton.doSomeThing();//output: the enumeration method implements singletons}}Copy the code

Effective Java 2nd Edition

This approach is functionally similar to the public domain approach, but it is more concise, provides a serialization mechanism for free, and absolutely prevents multiple instantiations, even in the face of complex serialization or reflection attacks. Although this approach is not yet widely adopted, enumerations of single-element types have emerged as the best way to implement Singleton. — Effective Java Chinese Edition 2

Java and Patterns

In Java and Patterns, the authors write that using enumerations to implement singleton control is simpler, provides a serialization mechanism for free, and is fundamentally guaranteed by the JVM to absolutely prevent multiple instances. It is a simpler, more efficient, and safer way to implement singleton control.

2.6 summarize

We mainly covered the following ways to implement the singleton pattern:

  • Hungry Style (thread-safe)
  • Lazy (non-thread-safe thread-safe version of the synchronized keyword)
  • Lazy (double checked locked version)
  • Lazy (register/static inner class)
  • Hungry hungry (enumeration)

Reference:

Head First Design Patterns

Effective Java 2nd Edition

Design patterns: An in-depth understanding of singleton patterns

I’m Snailclimb, a little white guy with a 5-year goal to be an architect. Welcome to my wechat official account :”Java Interview Clearance Manual “(a warm wechat official account, looking forward to common progress with you ~~~ adhere to the original, share beautiful articles, share a variety of Java learning resources) :