This is my second article on getting started

preface

Recently have been looking at the source code of some framework, found very boring, some of the use of design patterns I am clueless, realize their level is limited, or put the source code for a while. There has been an idea before, all the design patterns re-collated learning again, improve their understanding of some framework source code. Take this opportunity to push yourself to catch up with all the design patterns in one breath.

Start with the singleton pattern

Of all the design patterns that Spring has influenced, the most familiar is the singleton pattern, so start with the singleton.

define

There is only one instance of a class, and the class can create its own pattern for this instance.

advantages

  • Reduce memory resources
  • Ensure the consistency of data content

disadvantages

  • The singleton pattern generally has no interface and is difficult to extend. If you want to extend, you need to modify the original code, which violates the open closed principle
  • Concurrent testing, singletons are bad for code debugging.

Application scenarios

  • For classes that need to be created frequently, consider using singletons
  • When a class requires only one object to be generated, such as the leader of a class, the ID number of each person, etc.
  • Some classes occupy a large number of resources or take a long time to create instances, and are often used.
  • When a class needs to be instantiated frequently and objects created are frequently destroyed, such as multithreaded thread pools, network connection pools, etc.
  • Objects that frequently access databases or files.
  • For some operations that control the hardware level, or that should be a single control logic on the system, if there are multiple instances, the system will be completely out of whack.
  • When objects need to be shared. Since the singleton pattern allows only one object to be created, sharing that object can save memory and speed up object access. For example, the configuration object on the Web and the connection pool of the database.

How to write a singleton

There are three things to notice about a singleton: 1. Privatize the constructor; 2. Create it yourself and store it with static variables; 3. Provide fetch methods (static methods)

The hungry type

code

/** * Hunhun-style * advantages: high efficiency, thread safety * Disadvantages: memory */
package com.yang.singleton;

public class Singleton01 {
    // Create it by yourself and save it as a static variable
    private static Singleton01 instance = new Singleton01();
    // Privatize the constructor
    private Singleton01(a) {}// Provide a fetching method
    public static Singleton01 getInstance(a) {
        returninstance; }}Copy the code

The test code

package com.yang.singleton;

class Test {
    public static void main(String[] args) {
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + ":" + Singleton01.getInstance());
        }, Thread 1 "").start();

        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + ":" + Singleton01.getInstance());
        }, Thread 2 "").start(); }}Copy the code

Print the result

thread1: com. Yang. Singleton. Singleton01 @ 6218 fb2e threads2: com.yang.singleton.Singleton01@6218fb2e
Copy the code

The test result is ok, the class was initialized when the class was loaded, so there is no thread safety problem, but there is a disadvantage, if the class is not used, it will always occupy memory resources, resulting in memory waste, can be further optimized

Further optimization

LanHanShi

code

/** * Slacker * advantages: create instance only when called * Disadvantages: thread unsafe */
package com.yang.singleton;

public class Singleton02 {

    private static Singleton02 instance = null;

    private Singleton02(a) {}// Modify to need to use, create objects, save memory space
    public static Singleton02 getInstance(a) {
        if (instance == null) {
            instance = new Singleton02();
        }
        returninstance; }}Copy the code

Print test results

thread1: com. Yang. Singleton. Singleton02 @ 6 dd2a02f threads2: com.yang.singleton.Singleton02@4920b564
Copy the code

As a result, two objects are created. If both threads call getInstance() at the same time, then instance == null is true for both threads and both threads create a new object.

Know the problem and make progress

/** * Slacker * advantages: thread safety * Disadvantages: poor performance */
package com.yang.singleton;

public class Singleton02 {

    private static Singleton02 instance = null;

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

Print test results

thread1: com. Yang. Singleton. Singleton02 @ 6 dd2a02f threads2: com.yang.singleton.Singleton02@6dd2a02f
Copy the code

Add the synchronized keyword to the getInstance() method. But it’s only necessary to lock the singleton the first time you instantiate it, and it feels unnecessary to queue it up every time you call getInstance(). Security is guaranteed, but performance is compromised.

Adjust the granularity of the lock

DCL(Double Check Lock)

code

/** * DCL(Double Check Lock
package com.yang.singleton;
public class Singleton02 {
    private volatile static Singleton02 instance = null;
    private Singleton02(a) {}public static Singleton02 getInstance(a) {
        // Check whether blocking is needed
        if (instance == null) {
            // Thread 1, thread 2... Wait here.
            synchronized (Singleton02.class) {
                // Check whether another thread has created an instance
                if (instance == null) {
                    instance = newSingleton02(); }}}returninstance; }}Copy the code

Print test results

thread1: com. Yang. Singleton. Singleton02 @ 6 dd2a02f threads2: com.yang.singleton.Singleton02@6dd2a02f
Copy the code

When multiple threads enter the method, first determine instance == null. If the value is true, then lock the next action. Otherwise, return the value directly. If so, return directly.

  • The first instance == null determines whether to block
  • The second instance == null determines whether another thread has created an instance

The observant will notice that not only is there two levels of null, but there is also the volatile keyword on variables

The role of volatile:

  • Ensure variable visibility
  • Disables reordering of instructions

Having locks creates performance problems

Is there a way to leave it unlocked?

Reorganize your thoughts. Starting with the hunghan style, a series of problems are derived from wanting to delay the creation of the instance to save memory, so there is no other way for us to create the instance on the call.

You can make use of static inner classes

Loaded when called

Static inner class

code

/** * Static inner class * advantages: thread-safe, creating an instance when called */
package com.yang.singleton;

public class Singleton03 {

    private Singleton03(a) {}public static Singleton03 getInstance(a) {
        return InnerSingleton.INSTANCE;
    }
    private static class InnerSingleton {
        private static final Singleton03 INSTANCE = newSingleton03(); }}Copy the code

Print test results

thread2: com. Yang. Singleton. User @ 4920 b564 threads1: com.yang.singleton.User@4920b564
Copy the code

At this point, the singletons can come to an end

The following is a supplement to several online collection of singletons

The enumeration

/** * enumeration * benefits: thread safety, prevents deserialization */
package com.yang.singleton;

public class User {
    // Private constructor
    private User(a) {}// Define a static enumeration class
    static enum SingletonEnum {
        // Create an enumeration object that is born as a singleton
        INSTANCE;
        private User user;
        // Privatize the constructor of the enumeration
        private SingletonEnum(a) {
            user = new User();
        }
        public User getInstnce(a) {
            returnuser; }}// Expose a static method to get the User object
    public static User getInstance(a) {
        returnSingletonEnum.INSTANCE.getInstnce(); }}Copy the code

Container singleton

code

/ container type singleton * * * * implementation approach: preserve objects in a Map, when you need to use to take in the Map, if any, are directly out and haven't returned to create a use, on the Map, and then back out. (Equivalent to a simple Spring-managed container) */
package com.yang.singleton;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ContainerSingleton {

    private ContainerSingleton(a) {}private static Map<String, Object> ioc = new ConcurrentHashMap<String, Object>();

    public static Object getInstance(String className) {
        Object instance = null;
        if(! ioc.containsKey(className)) {try {
                instance = Class.forName(className).newInstance();
                ioc.put(className, instance);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return instance;
        } else {
            returnioc.get(className); }}}Copy the code

The test case

package com.yang.singleton;

class Test {
    public static void main(String[] args) {
        Object instance1 = ContainerSingleton.getInstance("com.yang.singleton.TestPojo");
        Object instance2 = ContainerSingleton.getInstance("com.yang.singleton.TestPojo");
        System.out.println("instance1: " + instance1);
        System.out.println("instance2: "+ instance2); }}Copy the code

Print test results

instance1: com.yang.singleton.TestPojo@61bbe9ba
instance2: com.yang.singleton.TestPojo@61bbe9ba
Copy the code

However, the above example has thread-safety issues

Look at the results in a multi-threaded environment

package com.yang.singleton;

class Test {
    public static void main(String[] args) {
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + ":" + ContainerSingleton.getInstance("com.yang.singleton.TestPojo"));
        }, Thread 1 "").start();

        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + ":" + ContainerSingleton.getInstance("com.yang.singleton.TestPojo"));
        }, Thread 2 "").start(); }}Copy the code

Print test results

thread1: com. Yang. Singleton. TestPojo @ 6 dd2a02f threads2: com.yang.singleton.TestPojo@4920b564
Copy the code

A simple modification

package com.yang.singleton;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ContainerSingleton {

    private ContainerSingleton(a) {}private volatile static Map<String, Object> ioc = new ConcurrentHashMap<String, Object>();

    public static Object getInstance(String className) {
        Object instance = null;
        // If the container does not contain a lock, determine if it needs to be locked
        if(! ioc.containsKey(className)) {synchronized (ContainerSingleton.class) {
                if(! ioc.containsKey(className)) {try {
                        instance = Class.forName(className).newInstance();
                        ioc.put(className, instance);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    return instance;
                } else {
                    returnioc.get(className); }}}else {
            returnioc.get(className); }}}Copy the code

Print test results

thread1Com. Yang. Singleton. 43 d83830 TestPojo @ threads2: com.yang.singleton.TestPojo@43d83830
Copy the code

CAS singleton

/** * CAS singleinstance /** * CAS singleinstance /** * CAS singleinstance /* /
package com.yang.singleton;

import java.util.concurrent.atomic.AtomicReference;

public class CASSingleton {
    private static final AtomicReference<CASSingleton> INSTANCE = new AtomicReference<CASSingleton>();

    private CASSingleton(a) {}public static CASSingleton getInstance(a) {
        for(; ;) { CASSingleton singleton = INSTANCE.get();if(singleton ! =null) { 
                return singleton;
            }
            singleton = new CASSingleton();
            if (INSTANCE.compareAndSet(null, singleton)) {
                returnsingleton; }}}}Copy the code

Extended volatile action

This is not the point of this article, but just to explain it briefly, check out the documentation if you want to know more about it. Visibility: Literally, when one thread changes the value of a variable, other threads holding the variable can immediately sense it, avoiding dirty reads. Instruction reordering: Java code is eventually converted to bytecode, which the JVM interprets and translates into individual machine instructions.

The process of creating an object is roughly divided into the following steps:

  1. The memory space was allocated. Procedure
  2. Initialize an object
  3. Set the object (instance) to point to the memory address just allocated

The normal execution order is 1–>2–>3, but if 2 takes too long to initialize the object, the compiler will switch the order of 2 and 3, so that the final execution order is 1–>3–>2. When 3 is executed, the other thread will determine that instance == null is not valid. Volatile the NPE to prevent 2 and 3 from swapping orders, thus avoiding THE NPE. The result is a thread-safe, memory-saving singleton.