This is the third day of my participation in the August More Text Challenge

The singleton pattern is one of the design patterns, but also the Java interview in the high frequency examination point, this article to summarize the Java singleton pattern various writing methods.

This section describes the singleton mode

The role of the singleton pattern:

  • To save memory and computation, instances are created only once instead of repeatedly
  • Ensure that the results are correct, such as singleton counters, used for multithreading statistics
  • Easy to manage, such as the date utility class, string utility class, do not need to create as many instances

The singleton mode applies to the following scenarios:

  • Stateless utility classes: For example, the logging utility class, wherever applicable, only needs to help us to log information, other than that, there is no need to store any state on its instance object, in this case we only need one instance object.
  • Global information classes: For example, if we log visits to A website on A class, we don’t want some visits to be logged on object A and others on object B, so let’s make this class A singleton.

Eight ways to write a singleton pattern

Hungry — Static constant (available)

public class Singleton1 {
    private final static Singleton1 INSTANCE = new Singleton1();

    private Singleton1(a) {}public static Singleton1 getInstance(a) {
        returnINSTANCE; }}Copy the code

Advantages: This method is relatively simple, because instantiation is done at class load time, avoiding thread synchronization issues.

Disadvantages: Instantiation is done when the class is loaded, not lazy loading, which wastes memory if the instance is not used from start to finish

Hungry — Static code block (available)

public class Singleton2 {

    private final static Singleton2 INSTANCE;

    static {
        INSTANCE = new Singleton2();
    }

    private Singleton2(a) {}public static Singleton2 getInstance(a) {
        returnINSTANCE; }}Copy the code

This method is similar to the above method, except that the class instantiation process is placed in the static code block, also when the class is loaded, the code in the static code block, initialization of the class instance. The pros and cons are the same as above.

Lazy — Threads are not safe (not available)

public class Singleton3 {
    private static Singleton3 instance;

    private Singleton3(a) {}public static Singleton3 getInstance(a) {
        if (instance == null) {
            instance = new Singleton3();
        }
        returninstance; }}Copy the code

This provides a lazy loading effect, but can only be used in a single thread. If, in multithreading, one thread enters an if(singleton== NULL) statement block and another thread passes the statement before it can proceed, instances are created multiple times. Therefore, this approach cannot be used in multithreaded environments.

Lazy — thread-safe, synchronous approach (not recommended)

public class Singleton4 {
    private static Singleton4 instance;

    private Singleton4(a) {}public synchronized static Singleton4 getInstance(a) {
        if (instance == null) {
            instance = new Singleton4();
        }
        returninstance; }}Copy the code

To solve the thread-unsafe problem of the third method above, we simply do a thread synchronization, which we did with the getInstance() method.

Disadvantages: Inefficient, every thread that wants to get an instance of a class has to execute getInstance() in sync. If you want to get an instance of the class, return it. Method synchronization efficiency is too low to improve.

Lazy — Thread unsafe, synchronized code blocks (not recommended)

public class Singleton5 {
    private static Singleton5 instance;

    private Singleton5(a) {}public static Singleton5 getInstance(a) {
        if (instance == null) {
            synchronized (Singleton5.class) {
                instance = newSingleton5(); }}returninstance; }}Copy the code

Because the fourth implementation method is too inefficient to synchronize, the synchronization method is discarded in favor of synchronized generation of instantiated code blocks. But this synchronization does not function as thread synchronization. As with the third implementation, adding a thread to the if(singleton==null) statement block creates multiple instances when another thread passes the statement before it can proceed.

Double check (recommended for interviews)

public class Singleton6 {
    private volatile static Singleton6 instance;

    private Singleton6(a) {}public static Singleton6 getInstance(a) {
        if (instance == null) {
            synchronized (Singleton6.class) {
                if (instance == null) {
                    instance = newSingleton6(); }}}returninstance; }}Copy the code

Advantages: Thread safety, lazy loading, high efficiency

Benefit of double-checking: Thread safety and performance

Purpose of volatile: Object creation is not atomic, and reordering is prevented

Static inner classes (available)

public class Singleton7 {
    private Singleton7(a) {}private static class SingletonInstance {

        private static final Singleton7 INSTANCE = new Singleton7();
    }

    public static Singleton7 getInstance(a) {
        returnSingletonInstance.INSTANCE; }}Copy the code

The static inner class is not instantiated immediately when the Singleton class is loaded. Instead, the SingletonInstance class is loaded when the Singleton class needs to be instantiated by calling getInstance.

The static properties of a class are initialized only when the class is first loaded. The JVM helps us keep our threads safe from entering the class while it is initialized.

Advantages: Avoid thread insecurity, delay loading, high efficiency.

Enumeration (recommended)

public enum  Singleton8 {
    INSTANCE;

    public void getInstance(a) {}}Copy the code

Not only does it avoid multi-threaded synchronization issues and lazy loading, but it also prevents deserialization from recreating new objects.

A comparison of different ways of writing it

  • Hungry: Simple, but no lazy loading
  • Lazy: There are thread safety issues
  • Static inner classes: available
  • Double check: for an interview
  • Enumeration: best

The application of various writing methods

  • The best approach is to take advantage of enumerations, because you can also prevent deserialization from recreating new objects
  • Methods that are not thread synchronized cannot be used
  • If the program starts with too many resources to load, then lazy loading should be used
  • The hanky-hank style does not apply if the creation of an object requires a configuration file
  • Lazy loading is good, but static inner classes introduce programming complexity, and enumerations are recommended in most cases.