为了账号安全,请及时绑定邮箱和手机立即绑定

漫画设计模式:每周一篇设计模式之单例模式

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
【首先不管何种形式实现单例模式,构造方法一定是私有的,这是大前提。】

饿汉模式
饿汉模式中的类实例是当类被加载时就被初始化出来的,所以在应用初始化时,会占用不必要的内存。同时,由于该实例在类被加载的时候就创建出来了,所以他是线程安全的。因为类的初始化是由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来阻止指令重排,从而避免上面问题的发生。

在这里插入图片描述
在这里插入图片描述


程序员小安(id:gh_77cfe3d539e8)
作者:程序员小安,8年Android开发,某互联网公司技术经理,csdn博客专家,主要分享个人的原创文章,技术包括Android性能优化,源码分析,设计模式,Flutter,算法等。欢迎关注,精彩文章定期推送。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
移动开发工程师
手记
粉丝
0
获赞与收藏
0

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消