Author: DeppWang, original address

In life, who does not interview. Singleton mode: a do not understand additional points, do not understand the points of knowledge

Another blog post by the dozen, but do you really understand? Check it out. After that, you write one.

Singleton is a favorite interview question. We often think we understand everything and that there are no more questions. There are all kinds of little mistakes that can happen when you write it out by hand, so here’s a quick and accurate way to write a singleton pattern.

Singleton patterns are written in various ways, such as “double check”, “hungry”, and “full”, which are always difficult to remember and distinguish. That’s right, human memory is limited, we should remember the most basic singleton pattern how to write.

Singleton pattern: A class can have only one object (instance). Three key points of the singleton pattern:

  1. External instances cannot be created using the new keyword (constructor), so constructors are private:private Singleton(){}
  2. Only class methods can get instances, so the methods are public and static:public static Singleton getInstance()
  3. There can be only one instance, and that can only be “data” for class variables, which are static (another memory: static methods can only use static variables) :private static Singleton instance

First, the most basic, the simplest way to write

A new instance is created when the class is loaded

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

    private Singleton() {
    }

    public static Singleton getInstance() {
        return instance;
    }

    public void show(){
        System.out.println("Singleon using static initialization in Java");
    }
}

// Here is how to access this Singleton class
Singleton.getInstance().show();Copy the code

When singleton.getInstance () is executed, the class loader loads singleton.class into the virtual machine, which allocates a chunk of memory in the method area (metadata area) for the class variable and assigns it a null value. Execute the

() method to create a new instance pointing to the class variable instance. This process takes place during class loading and is thread-safe by the virtual machine. So getInstance() is thread-safe because the instance already exists before it is executed.

Many blog posts say that instance also needs to be declared final, which it doesn’t. Final is immutable so that a reference to instance cannot point to another instance, which is not useful here. Plus, of course.

One disadvantage of this approach is that if you need to set the instance by parameter, you cannot do so. Here’s an example:

class Singleton { private static Singleton instance = new Singleton(); Private Singleton() {} public static Singleton getInstance(String name) { return instance; } public void show(){ System.out.println("Singleon using static initialization in Java"); } } // Here is how to access this Singleton class Singleton.getInstance(String name).show();Copy the code

Two, can be set through the parameters of the instance writing method

With this in mind, a new instance is created when the getInstance() method is called.

public class Singleton {
    private static Singleton instance;

    private String name;

    private Singleton(String name) {
        this.name = name;
    }

    public static synchronized Singleton getInstance(String name) {
        if (instance == null) {
            instance = new Singleton(name);
        }
        return instance;
    }

    public String show() {
        return name;
    }
}

Singleton.getInstance(String name).show();Copy the code

The synchronized keyword is added here to ensure that only one instance will be generated, but the efficiency is not high. After the instance is created successfully, the lock is not needed to obtain the instance.

What happens when synchronized is not added:

Instance is a class variable. The class is stored in the method area (metadata area), which is shared by threads, so the class variable instance is shared by threads, and the class variable is also in main memory. When the thread executes getInstance(), it creates a stack frame in its own working memory and copies the main memory instance to the working memory. Instance == null; instance == null; instance = null; instance = null;

Three, the improved version of the lock writing

The implementation locks only when it is created and does not lock when it is acquired.

public class Singleton { private static volatile Singleton instance; private Singleton() { } public static Singleton getInstance() { if (instance == null) { synchronized(Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; }}Copy the code

Why judge twice:

Multiple threads copy instance into the working memory, i.e. multiple threads read instance == NULL. Although only one thread enters the synchronized method at a time, when the entering thread successfully creates an instance, Synchronized guarantees visibility (the variable is written back to main memory before unlock), at which point instance is not null, but another thread is already there, and a thread enters the synchronized method again. If not, a new instance is created.

Why use volatile to modify instance:

Synchronized can achieve atomicity, visibility and order. Atomicity is achieved: only one thread at a time executes the synchronized block of code. But computers, in order to run more efficiently, order reordering.

Instance = new Singleton(); Will be split into 3 steps.

  • A: Allocate A block of memory space
  • B: Create a new instance in memory space
  • C: Refers a reference to an instance, that is, to the address of the memory space where the instance is stored

If instance is inside synchronized, then there is no problem. The problem is that instance is outside synchronized, because a bunch of hungry threads are waiting for an instance.

Simulate an instruction reorder error scenario: In a multithreaded environment, just one thread executes ACB in a synchronized block, and when it reaches AC (and writes instance back to main memory), another thread executes the first judgment, considers instance not null, and returns instance, However, instance has not been initialized correctly, so an error occurs.

When an instance is volatile, no other thread can read it until the ACB completes

Why does volatile prevent instruction reordering by adding a lock instruction after ACB, so that subsequent operations can be performed only after the previous operation has been performed

You may think the above explanation is too complicated to understand. Yeah, it was complicated, and it took me a long time to figure it out. You can see if this is a better understanding of one of the preempt principles of the Java Virtual Machine specification: for volatile variables, reads must wait for writes to complete.

Other non-mainstream writing methods

Enumeration:

public enum EasySingleton{
    INSTANCE;
}Copy the code

When an interviewer asks me to write a singleton, I always feel a little out of place

Static inner class:

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

Five, the summary

The singleton pattern is used to create Spring container beans to save memory.

The singleton pattern is not written, which is fine, because you may not be able to answer the next question :).

6. Read more

  • How to write a singleton correctly
  • How to create thread safe Singleton in Java
  • Why Enum Singleton are better in Java
  • On design patterns: When should I use the singleton?

This article is published by OpenWrite!