单例模式(饿汉/懒汉)

文章介绍了单例模式的两种实现方式——饿汉模式和懒汉模式,讨论了它们的优缺点以及线程安全问题。饿汉模式在类加载时创建实例,线程安全但可能会浪费资源;懒汉模式首次使用时创建实例,更高效但线程不安全。为了解决懒汉模式的线程安全问题,可以使用synchronized关键字保证原子性或使用Volatile防止指令重排序。同时,文章强调了设计模式在提升代码质量中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

单例模式: 饿汉模式/懒汉模式
        单例模式是一种常见的设计模式,类似于棋谱,把一些对局中常见场景 ,整理总结,让别人来研究学习。
设计模式有多种,以后有些工作经验再学习 ,有了一定的经验才能明白其中的道理
为什么会有设计模式
        代码领域里程序猿水平参差不齐, 于是就有大佬们,根据一些常见的需求场景, 整理出来了一些应对的解决办法, 这个东西就是"设计模式"。 能够提高程序猿的“下限”按照设计模式来写代码,不说代码能写多好,但是不会很差。
饿汉模式:
类加载的同时, 创建实例。
懒汉模式:
效率更高
类加载的时候不创建实例. 第一次使用的时候才创建实例
        如果整个代码后续没人调用getInstance, 与 饿汉模式 相比,这样就把构造实例的过程省去了, 或者即使有代码后续调用getInstance,但是调用的时机比较晚,这个时候创建实例的时机也就较晚,就 和其他的耗时操作错开了,使系统执行更加高效。(一般程序刚启动的时候,要初始化的东西很多,系统资源紧张)
饿汉模式线程安全,懒汉不安全
饿汉模式:线程安全
这个操作只是单纯的“读数据" ,不涉及到修改。
懒汉模式:线程不安全
此时懒汉模式会创建多个实例不符合单例模式要求出现BUG
如何解决懒汉模式线程不安全问题?
(1)加锁:synchronized,保证原子性
(2)关键字:Volatile,内存可见性,禁止指令重排序
 加锁
        通过 synchronized 加锁方式,线程安全问题解决了。 但是又引入了新的问题, 线程不安全,也不是一直都不安全。只是实例创建之前(首轮调用的时候) 才会触发线程不安全问题,在第一次 实例创建好之后,instance不为null也就不涉及修改操作,此时线程也就安全了。
        由于 , 加锁开销其实较大 ,加锁可能就会涉及到用户态->内核态之间的切换,这样的切换成本是比较高的。因此 加锁绝对不是“无脑加”,在我们 只在需要时加锁。
加锁代价比较大,因此该加锁枷锁,不该加就去掉。
只需要在加锁前面加一个判断,判断是否需要加锁
Volatile关键字:
指令重排序(编译器进行代码优化操作):造成线程不安全
new操作本质上也分成三个步骤:
1.申请内存,得到内存首地址。
2.调用构造方法来初始化实例。
3.把内存的首地址赋值给instance引用。
        上述步骤可能编译器会进行“指令重排序”的优化, 在单线程的角度下, 2和3是可以调换顺序的。 (单线程的情况下,此时2和3先执行谁,后执行谁效果样)
设想:
        假设此处触发指令重排序, 并且按照1  3  2的顺序执行的。 有可能t1执行了1和3之后,instance得到了不完全的对象,只是有内存,但是内存上的数据是无效的。接下来t2线程执行调用getInstance方法,判断instance不为null,直接返回instance出现了BUG,造成线程不安全。
因此就要用Volatile关键字禁止指令重排序
完整单例模式的懒汉实现:
        另一方面,再想想, 如果线程更多的话, 有的线程在读Instance,有的线程在修改, 也存在内存可见性问题, 加上volatile也是应当。
简单完整单例模式代码如下:
class SingletonLazy{
   private volatile static SingletonLazy instance = null;
    //类加载的时候不创建实例. 第一次使用的时候才创建实例
    public static SingletonLazy getInstance(){
        if(instance == null){//判断是否加锁
            //给SingletonLazy单例类加锁
            synchronized (SingletonLazy.class){
                if(instance == null){//判断是否创建实例
                    instance = new SingletonLazy();
                }

            }
        }
        return instance;
    }
    //将此类构造方法设置为private,此时就无法在此类外创建实例对象
    private SingletonLazy(){  }
}


public class Singleton {
    public static void main(String[] args) {
        SingletonLazy instance = SingletonLazy.getInstance();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值