Design Patterns - Creational Patterns_7 Implementations of Singleton Patterns

Creational patterns

Creational patterns provide a mechanism for creating objects, which can improve the flexibility and reusability of existing code.

Types ofImplementation points
factory methodDefine an interface for creating objects, and let its subclasses decide which factory class to instantiate. The factory mode delays the creation process to subclasses.
abstract factoryProvides an interface for creating a series of related or interdependent objects without specifying their concrete classes.
builderDecouples a complex build from its representation so that the same build process can create different representations
prototypeUse prototype instances to specify the type of object to be created, and create new objects by copying these prototypes.
singletonEnsures that there is only one instance of a class and provides a global access point to it.

overview

In the design pattern, there are three categories according to different processing methods:

  • Creational patterns
  • structural pattern
  • behavior pattern

Among them, the creation mode has already implemented four of them:

  • Factory Method Pattern
  • Abstract factory pattern
  • builder mode
  • prototype pattern
  • In addition to this, there is one last singleton pattern


The singleton mode can be said to be one of the simplest modes in the entire design. In programming development, such a scenario is often encountered, that is, it is necessary to ensure that there is only one instance of a class even if multiple threads access it at the same time, and it is necessary to provide a point of global access to this instance.

The singleton mode mainly solves the frequent creation and consumption of a globally used class, thereby improving the overall code performance.

Case

What you can see in daily development, for example:

  1. The connection pool of the database will not be created repeatedly
  2. Generation and use of a singleton pattern bean in spring
  3. In our usual code, we need to set some global attributes to save

In our daily development, the singleton mode will be used in the above scenarios. Although the singleton mode is not complicated, it has a wide range of applications.

7 singleton pattern implementations

Design Patterns - Creational Patterns_ Singleton Pattern s

Concurrent Programming-09 Safe Publishing Object + Singleton Mode Detailed Explanation

There are many ways to implement the singleton mode, mainly in whether it supports the lazy mode and whether it is thread-safe.

Of course, there are also some scenarios that do not need to consider lazy loading, that is, lazy mode, and will directly use static static classes or properties and methods for external calls.

Then we will implement the singleton mode in different ways.

Static class use

public class Singleton_00 {

    public static Map<String,String> cache = new ConcurrentHashMap<String, String>();

}

  • The above method is very common in ordinary business development, so that the static class method can directly initialize the Map class when it is run for the first time, and here we do not need to use lazy loading .
  • It is more convenient to use static classes only for global access without maintaining any state.
  • But if it needs to be inherited and some specific state needs to be maintained, it is suitable to use the singleton mode.

Lazy mode (thread unsafe)

public class Singleton_01 {

    private static Singleton_01 instance;

    private Singleton_01() {
    }

    public static Singleton_01 getInstance(){
        if (null != instance) return instance;
        return new Singleton_01();
    }

}

  • One of the characteristics of the singleton mode is that it does not allow direct external creation, that is, new Singleton_01() , so here a private attribute private is added to the default constructor.
  • At present, the singleton in this way does satisfy lazy loading, but if there are multiple visitors to obtain the object instance at the same time, it will cause multiple identical instances to coexist, thus failing to meet the requirements of the singleton.

lazy pattern (thread safe)

public class Singleton_02 {

    private static Singleton_02 instance;

    private Singleton_02() {
    }

    public static synchronized Singleton_02 getInstance(){
        if (null != instance) return instance;
        return new Singleton_02();
    }

}

Although this mode is safe, but after the lock is added to the method, all accesses will waste resources due to the need for lock occupation. Unless it is a special case, it is not recommended to implement the singleton mode in this way.

Hungry man mode (thread safety)

public class Singleton_03 {

    private static Singleton_03 instance = new Singleton_03();

    private Singleton_03() {
    }

    public static Singleton_03 getInstance() {
        return instance;
    }

}

  • This method is basically the same as the first instantiated Map at the beginning. It is loaded directly when the program starts, and can be obtained when there is an external need for subsequent use.

  • But this method is not lazy loading, that is to say, no matter whether such a class is used in the program, it will be created at the beginning of the program startup.

Use the inner class of the class (thread-safe)

public class Singleton_04 {

    private static class SingletonHolder {
        private static Singleton_04 instance = new Singleton_04();
    }

    private Singleton_04() {
    }

    public static Singleton_04 getInstance() {
        return SingletonHolder.instance;
    }

}

  • The singleton mode implemented by using the static inner class of the class not only ensures thread safety and lazy loading, but also does not consume performance due to locking.
  • This is mainly because the JVM virtual machine can guarantee the correctness of multi-threaded concurrent access, that is, the construction method of a class can be correctly loaded in a multi-threaded environment.
  • This method is also a highly recommended singleton pattern

Double lock check (thread safety)

public class Singleton_05 {

    private static Singleton_05 instance;

    private Singleton_05() {
    }

    public static Singleton_05 getInstance(){
       if(null != instance) return instance;
       synchronized (Singleton_05.class){
           if (null == instance){
               instance = new Singleton_05();
           }
       }
       return instance;
    }

}

  • The double lock method is an optimization of method-level locks, which reduces the time-consuming part of acquiring instances.
  • At the same time, this method also satisfies lazy loading.

CAS "AtomicReference" (thread safety)

import java.util.concurrent.atomic.AtomicReference;

public class Singleton_06 {

    private static final AtomicReference<Singleton_06> INSTANCE = new AtomicReference<Singleton_06>();

    private static Singleton_06 instance;

    private Singleton_06() {
    }

    public static final Singleton_06 getInstance() {
        for (; ; ) {
            Singleton_06 instance = INSTANCE.get();
            if (null != instance) return instance;
            INSTANCE.compareAndSet(null, new Singleton_06());
            return INSTANCE.get();
        }
    }

    public static void main(String[] args) {
        System.out.println(Singleton_06.getInstance());  
        System.out.println(Singleton_06.getInstance()); 
    }


}
  • The java concurrency library provides many atomic classes to support concurrent access data security AtomicInteger , AtomicBoolean , AtomicLong , AtomicReference .
  • AtomicReference can encapsulate and reference a V instance, and the singleton method that supports concurrent access uses such a feature.
  • The advantage of using CAS is that it does not need to use the traditional locking method to ensure thread safety, but relies on the busy-wait algorithm of CAS and the implementation of the underlying hardware to ensure thread safety. Compared with other lock implementations, there is no additional overhead for thread switching and blocking, and it can support greater concurrency.
  • Of course, CAS also has a disadvantage that it is busy waiting. If it has not been obtained, it will be in an endless loop.

Enum singleton (thread-safe) recommended by Effective Java author

public enum Singleton_07 {

    INSTANCE;
    public void test(){
        System.out.println("hi~");
    }

}

Joshua Bloch (English: Joshua J. Bloch, August 28, 1961 -), a famous American programmer. He designed and implemented many functions for the Java platform, and served as Google's Chief Java Architect (Chief Java Architect).

  • The author of Effective Java recommends using enumeration to solve the singleton pattern, which may be the least used at ordinary times.
  • This approach solves the most important issues: thread safety, free serialization, and single instance.

Call method:

 @Test
    public void test() {
        Singleton_07.INSTANCE.test();
    }
  • This way of writing is similar to the public domain method in function, but it is more concise and provides a serialization mechanism for free, which absolutely prevents instantiation, even in the face of complex serialization or reflection attacks. when. Although this method has not been widely adopted, the single-element enumeration type has become the best way to implement Singleton.
  • But this method is not available in the case of inheritance.

summary

Here we see various skills such as lazy man, hungry man, thread safety, static class, inner class, locking, serialization, etc.

In normal development, if you can ensure that this class is globally available and you don't need to do lazy loading, you can just create it and call it externally.

But if there are many classes, and some of them need to be displayed after the user triggers certain conditions, then lazy loading must be used. Thread safety can be selected on demand.

Tags: Java Design Pattern Singleton pattern

Posted by cbrknight on Sat, 28 Jan 2023 09:52:16 +1030