专栏首页面试系列(java&Android)大厂面试必备之设计模式:漫画单例模式
原创

大厂面试必备之设计模式:漫画单例模式

【首先不管何种形式实现单例模式,构造方法一定是私有的,这是大前提。】

饿汉模式

饿汉模式中的类实例是当类被加载时就被初始化出来的,所以在应用初始化时,会占用不必要的内存。同时,由于该实例在类被加载的时候就创建出来了,所以他是线程安全的。因为类的初始化是由ClassLoader完成的,利用了ClassLoader的线程安全机制,ClassLoader的loadClass方法在加载类的时候使用了synchronized关键字实现线程同步。

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

instance对象在Singleton类被加载的时候,被实例化出来,他的实例化跟着类加载一起进行,很简单,保持了唯一性。

通过静态内部类来实现的饿汉模式

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

饿汉模式在类被加载时,就创建出对象,而通过静态内部类的方式,Singleton对象被加载时,INSTANCE没有被初始化,SingletonHolder类不会被加载,只有在调用getInstance()方法时,才会加载SingletonHolder类,实例化INSTANCE对象。由于类的初始化是由ClassLoader完成的,利用了ClassLoader的线程安全机制,所以通过静态内部类来实现的饿汉模式既不过早消耗资源,又能保证线程安全。

懒汉式

懒汉式顾名思义就是不会提前做准备,在使用的时候,才会实例化对象。

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  

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

上面的代码很简单,通过 if (instance == null) 判断是否已经存在instance对象,存在的话,直接返回,不存在,则实例出instance对象。

1)粗暴式加锁

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

可见在getInstance()方法上增加synchronized,通过锁就可以实现线程安全,但是这种形式加锁的范围是整个初始化方法,效率很低,因为加锁的目的是保证第一次创建对象是同步的,不是第一次创建对象的情况,没有必要进行同步,可以直接返回instance。当多个线程调用getInstance()方法时,全部在等第一个线程释放锁,效率不高。

2)双重锁

public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
    if (singleton == null) {  
        synchronized (Singleton.class) {  
        if (singleton == null) {  
            singleton = new Singleton();  
        }  
        }  
    }  
    return singleton;  
    }  
}  

相信双重锁对很多人来说并不陌生,尤其是经常面试的同学,单例双重锁是面试经常被问到的。

首先,通过使用同步代码块的方式减小了锁的范围,提高了效率,同时引入volatile阻止对象初始化的指令重排,实现多线程同步。

当执行singleton = new Singleton(); 语句时,正常会分下面三个步骤:

1)分配内存

2)初始化对象

3)将singleton指向分配的内存地址

可以看到,实例化对象并不是原子操作,并且编译器可能会指令重排,比如以上步骤被重排为下面的步骤:

1)分配内存

2)将singleton指向分配的内存地址

3)初始化对象

这样的话,如果线程A先分配内存,再singleton指向分配的内存地址 ,最后初始化对象时可能会出现如下情况:当线程A还没有执行3)初始化对象时,线程2执行到 if (singleton == null) 语句,因为线程A已经把singleton指向了内存地址,所以if (singleton == null) 语句返回false,getSingleton()方法则直接把未初始化的singleton对象返回回去,这个时候,线程B用到singleton对象时,就会出现空指针。我们通过volatile来阻止指令重排,从而避免上面问题的发生。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 面试官问我:Andriod为什么不能在子线程更新UI?

    看完《你为什么在现在的公司不离职?》,很多同学踏上了面试之路,作为颜值担当的天才少年_也开始了面试之路。

    天才少年
  • 大厂面试必备之设计模式:漫画策略模式

    官方定义不太好理解,我翻译一下,在策略模式中,会针对一个行为(比如支付),定义多个实现类,每个类都封装具体的实现算法,并且为了保证他们是同一行为,通常这些实现类...

    天才少年
  • 技术分析 | 谁是终极大Boss?一张图看懂《长安十二时辰 》人物关系

    豆瓣评分高达8.6的国产剧《长安十二时辰》,终于在今晚迎来大结局——幕后BOSS究竟是谁?张小敬和李必命运如何,都一一揭开谜底。该剧改编自以“脑洞大”著称的作家...

    逸迅科技
  • 万字长文总结机器学习的模型评估与调参

    选自 Python-Machine-Learning-Book On GitHub

    Sam Gor
  • 【推荐收藏】模型评估与调参(Python版)

    “管道工作流”这个概念可能有点陌生,其实可以理解为一个容器,然后把我们需要进行的操作都封装在这个管道里面进行操作,比如数据标准化、特征降维、主成分分析、模型预测...

    Sam Gor
  • 万字长文总结机器学习的模型评估与调参,附代码下载

    选自 Python-Machine-Learning-Book On GitHub

    用户2769421
  • 万字长文总结机器学习的模型评估与调参,附代码下载

    “管道工作流”这个概念可能有点陌生,其实可以理解为一个容器,然后把我们需要进行的操作都封装在这个管道里面进行操作,比如数据标准化、特征降维、主成分分析、模型预测...

    AI算法与图像处理
  • Machine Learning-模型评估与调参(完整版)

    选自 Python-Machine-Learning-Book On GitHub

    Sam Gor
  • C#设计模式Design Pattern示例之模板方法Template Method

    程序你好
  • 协程

    比如你的双手可以同时做两件事,比如吃饭这件事就是并发,吃饭这个过程中,可以同时吃几种菜,甚至喝汤,这个过程就是一个多任务并发的过程,但是并发在时间上是不能同时进...

    酷走天涯

扫码关注云+社区

领取腾讯云代金券