• 写一个项目中使用的单例模式


    分析&回答

    单例模式虽然比较多,但是项目中一般使用饿汉、双重校验锁两种方式。

    饿汉模式
    
    public class Singleton {
        private static Singleton instance = new Singleton();
    
        private Singleton() {
        }
    
        public static Singleton getInstance() {
            return instance;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    双重校验锁模式
    public class Singleton {
        private volatile static Singleton singleton;
    
        private Singleton() {
        }
    
        public static Singleton getSingleton() {
            if (singleton == null) {
                synchronized (Singleton.class) {      //1
                    if (singleton == null) {          //2
                        singleton = new Singleton();  //3
                    }
                }
            }
            return singleton;
        }
    }  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    反思&扩展

    为什么要用volatile?

    如果不用volatile,则因为内存模型允许所谓的“无序写入”,可能导致失败。——某个线程可能会获得一个未完全初始化的实例

    考察上述代码中的 //3 行。此行代码创建了一个 Singleton 对象并初始化变量 instance 来引用此对象。这行代码的问题是:在Singleton 构造函数体执行之前,变量instance 可能成为非 null 的!

    什么?这一说法可能让您始料未及,但事实确实如此。

    在解释这个现象如何发生前,请先暂时接受这一事实,我们先来考察一下双重检查锁定是如何被破坏的。假设上述代码执行以下事件序列:

    1. 线程 1 进入 getInstance() 方法。
    2. 由于 instance 为 null,线程 1 在 //1 处进入synchronized 块。
    3. 线程 1 前进到 //3 处,但在构造函数执行之前,使实例成为非null。
    4. 线程 1 被线程 2 预占。
    5. 线程 2 检查实例是否为 null。因为实例不为 null,线程 2 将instance 引用返回,返回一个构造完整但部分初始化了的Singleton 对象。
    6. 线程 2 被线程 1 预占。
    7. 线程 1 通过运行 Singleton 对象的构造函数并将引用返回给它,来完成对该对象的初始化。

    加上volatile的作用主要是防止指令重排,主要是说,无论什么情况,对于volatile变量的写操作必须在完成后才能读取,所以不会出现未完成构造就读取的情况。


    喵呜面试助手: 一站式解决面试问题,你可以搜索微信小程序 [喵呜面试助手] 或关注 [喵呜刷题] -> 面试助手 免费刷题。如有好的面试知识或技巧期待您的共享!

  • 相关阅读:
    NEDC、WLTC、CLTC,三种汽车能源消耗测试标准有什么区别?
    工作小记系列3-Minikube搭建笔记
    微信分账-添加分账接收方v3
    五、Midway 中环境变量的使用
    「Verilog学习笔记」使用3-8译码器①实现逻辑函数
    ES6 入门教程 10 对象的扩展 10.2 属性名表达式 & 10.3 方法的name 属性
    电脑重装系统后如何在防火墙设置允许浏览器访问网络
    数据的力量:Facebook如何通过数据分析驱动创新
    jmeter单接口和多接口测试
    使用go的并发性来解决Hilbert酒店问题
  • 原文地址:https://blog.csdn.net/jjclove/article/details/125718518