java design pattern -- singleton pattern -- thread-safe lazy style

A design pattern is a set of commonly used, catalogued, code-design experiences that are known to most people.
Design patterns are used to reusable code, make code easier to understand by others, and ensure code reliability.

In the development process, we only need one of some objects, such as: configuration file, tool class, thread pool, cache, log object, etc. If these objects are created with multiple instances, it will cause many problems, such as taking up too many resources, inconsistent reading and writing files, etc.
How to ensure that there is only one instance of a certain instance in the entire application? ——Singleton pattern Hungry Chinese implementation of singleton pattern

public class Singleton {
    //1.构造方法私有化,不允许外部直接创建对象
    private Singleton(){}
    //2.创建类的唯一实例---该类只要一加载就会创造一个唯一实例---饿汉式实现
    private static Singleton instance = new Singleton();
    //3.提供一个用于获取实例的方法
    public static Singleton getInstance() {
        return instance;
    }
}

Hungry style will create a static object of this class during the class loading process, so its loading process is slower than lazy style, but the process of obtaining class instances is faster than lazy style, and it is safer in multi-threaded mode. The disadvantage is that it is loaded before the method is called, which occupies more memory.
Lazy implementation of the singleton pattern

public class SingletonII {
    private SingletonII() {
    }

    private static SingletonII instance;

    public static SingletonII getInstance() {
        if (instance == null) {//1:读取instance的值
            instance = new SingletonII();//2: 实例化instance
        }
        return instance;
    }
}
测试类:
/**
 * Created by dell on 2018/4/15.
 * 饿汉模式加载类时比较慢,运行时,获取对象的速度比较快。因为加载类时已经创建了类的唯一实例,线程安全。
 * 懒汉式加载类时比较快,运行时,获取对象的速度比较慢,而且有可能线程不安全。
 */
public class SingleTest {

    public static void main(String[] args) {
        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();
        if (s1 == s2) {
            System.out.println("s1和s2指向同一个实例");
        } else {
            System.out.println("s1和s2指向不同的实例");
        }
        SingletonII s3 = SingletonII.getInstance();
        SingletonII s4 = SingletonII.getInstance();
        if (s3 == s4) {
            System.out.println("s3和s4指向同一个实例");
        } else {
            System.out.println("s3和s4指向不同的实例");
        }

        //检查懒汉式线程安全问题
        //newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
        ExecutorService threadPool = Executors.newFixedThreadPool(1000);
        for (int i = 0; i < 1000; i++) {
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+":"+SingletonII.getInstance());
                }
            });
        }
    }
}

For the comment part of the SingletonII code, if there are two threads at this time, thread A executes to 1, reads the instance as null, and then the cpu is robbed by thread B. At this time, thread A has not instantiated the instance. . Therefore, when thread B reads the instance, it is still null, so it instantiates the instance. Then, the cpu is robbed by thread A. At this point, since thread A has read the value of instance and thinks it is null, it instantiates the instance again. So, thread A and thread B are not returning the same instance.
Caused the thread insecurity of the singleton pattern.
Solution: use synchronized to modify the method of obtaining an instance

/**
 * Created by dell on 2018/4/15.
 * 单例模式的懒汉式实现
 */
public class SingletonII {
    private SingletonII() {
    }

    private static volatile SingletonII instance;
    //双重检查加锁机制
    public static synchronized SingletonII getInstance() {
        if (instance == null) {
            synchronized (SingletonII.class) {
                if (instance == null) {
                    instance = new SingletonII();
                }
            }
        }
        return instance;
    }
}

The double locking mechanism cooperates with the volatile keyword.
instance = new SingletonII() can actually be divided into the following steps:
1. Apply for a memory space;
2. Instantiate the object in this space;
3. The reference of instance points to the address of this space;
the problem of instruction reordering is that :
For the above steps, it is very likely that the instruction reordering is not performed sequentially according to the above 123 steps. For example, first execute 1 to apply for a memory space, and then execute 3 steps. The reference of instance points to the address of the memory space just applied. Then, when it executes 2 steps to judge the instance, since the instance has already pointed to a certain address, It will no longer be null, and therefore, the object will not be instantiated. This is the so-called instruction reordering safety problem. So, how to solve this problem? Add the volatile keyword, because volatile can prohibit instruction reordering.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324571858&siteId=291194637