Java multi-threading ~ implements the singleton mode in two modes: "hungry man" and "lazy man"

content

What is the singleton pattern?

How to implement the singleton pattern through code?

hungry man mode

lazy mode

Single thread version

Multithreaded version

double check method

What is the singleton pattern?

The singleton pattern is a type of design pattern. The singleton pattern means that when a class is provided for use elsewhere, only the same instantiated object can be used. The singleton pattern can ensure that there is only one instance of a class in the program, and multiple instances will not be created.

How to implement the singleton pattern through code?

Implemented using a private constructor

①Hungry Chinese writing method: assign a value when a static variable is initialized

②Lazy writing method: no assignment is made during initialization, and if it is not initialized and then initialized when it is used, this writing method can be said to be thread-unsafe, or it can be said to be thread-safe but inefficient

③Double check lock (the most important way to write)

hungry man mode

Use a private constructor to implement the singleton pattern, and assign values ​​when the static variables are initialized

/**
 * Created with IntelliJ IDEA.
 * Description: Singleton mode, written in Hungry Chinese style
 */
public class HungryManStyle {
    private static HungryManStyle instance = new HungryManStyle();

    private HungryManStyle(){}

    public static HungryManStyle getInstance() {
        return instance;
    }
}

Verify the use of the singleton pattern

    public static void main(String[] args) {
        //Mock objects using singletons
        HungryManStyle instance1 = HungryManStyle.getInstance();
        HungryManStyle instance2 = HungryManStyle.getInstance();
        System.out.println(instance1 == instance2);
    }

 

lazy mode

Single thread version

An instance is not created when a class is loaded, and an instance is created when it is used for the first time. Use the if statement to determine whether the instance is empty. If it is empty, it is the first use, and an instance object is created for it. Otherwise, the object is returned directly.

/**
 * Created with IntelliJ IDEA.
 * Description: Singleton Mode: Lazy-style writing single-threaded
 */
public class LazyMode {
    private static LazyMode instance;
    private LazyMode(){}

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

Multithreaded version

The single-threaded version of lazy mode above is unsafe.

The thread insecurity problem occurs when the instance object is first created. If multiple threads call the getInstance method at the same time, multiple instances may be created

Once the instance has been created, there is no thread safety problem when calling the getInstance method in a multi-threaded environment later

In order to solve this thread insecurity problem, it needs to be locked to ensure that only one thread can call the getInstance method at a time. However, this method of writing will be very inefficient when the method is called in a multi-threaded environment after the instance is created. Only one thread can successfully apply for a lock at a time and obtain the instance object. Other threads can only wait for the thread to finish running. Only then can you compete for the lock to obtain the instance object.

/**
 * Created with IntelliJ IDEA.
 * Description: Singleton mode: lazy-man-style multi-threading
 */
public class LazyMode1 {
    private static LazyMode1 instance;
    private LazyMode1(){}

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

double check method

This writing method is an improvement on the lazy multi-threaded version writing method. Further judgment is made on the basis of locking, which not only ensures thread safety, but also improves the running efficiency.

Specific operations: ①Use double if judgment to reduce the frequency of lock competition; ②Add volatile to instance

/**
 * Created with IntelliJ IDEA.
 * Description: Singleton mode: double check lock write method
 */
public class DoubleCheckLock {
    private static volatile DoubleCheckLock instance;
    private DoubleCheckLock(){}
    public static DoubleCheckLock getInstance() {
        if(instance == null) {
            synchronized(DoubleCheckLock.class) {
                if(instance == null) {
                    instance = new DoubleCheckLock();
                }
            }
        }
        return instance;
    }
}

How to understand the above two operations

The locking/unlocking operation is a relatively expensive operation, and the thread insecurity of the lazy mode only occurs when the instance is created for the first time, so there is no need to lock it in the subsequent use, which can be very large. improve efficiency

The outer if is used to determine whether the instance object has been instantiated at present, and in order to ensure "visibility", volatile is added to the instance.

After the above two operations are completed, when multiple threads call the getInstance method for the first time, multiple threads will find that the instance is null, and then compete for the lock. In the end, only one thread successfully competes for the lock, and then completes the operation of creating the instance. When this instance is created, other threads competing for the lock will be blocked by the inner if judgment, and no other instances will be created. The subsequent threads do not need to be locked, and directly pass the outer if judgment statement It is known that the instance has been created, so the instance object is directly returned without applying for a lock operation, which reduces overhead and improves efficiency.

Tags: Java Multithreading Singleton pattern programming language

Posted by mikeatrpi on Fri, 20 May 2022 22:24:49 +0300