前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >线程安全的单例模式--“饿汉“,“懒汉“

线程安全的单例模式--“饿汉“,“懒汉“

作者头像
用户10921393
发布2024-07-19 08:24:45
90
发布2024-07-19 08:24:45
举报
文章被收录于专栏:Y.

1.什么是设计模式?

设计模式好⽐象棋中的"棋谱".红⽅当头炮,⿊⽅⻢来跳.针对红⽅的⼀些⾛法,⿊⽅应招的时候有⼀ 些固定的套路.按照套路来⾛局势就不会吃亏. 软件开发中也有很多常⻅的"问题场景".针对这些问题场景.

单例模式能保证某个类在程序中只存在唯⼀⼀份实例,⽽不会创建出多个实例.

2.饿汉模式

代码语言:javascript
复制
class Singleton {
    private static Singleton instance = new Singleton();
    //在这个类被加载的时候,就会初始化这个 静态变量

    public static Singleton getInstance() {
        return instance;
    }

    private Singleton(){}
    //后续再别的代码中,尝试new  Singleton,就会直接编译报错
}

对于饿汉来说,getInstance 直接返回instance实例, 这个本质上就是"读操作".所以即使是多个线程同时读取一个变量,线程也是安全的 

3.懒汉模式

代码语言:javascript
复制
class SingletonLazy {
    private static SingletonLazy instance = null;
    //这个指引指向唯一实例,想爱你初始化为空,而不是立即创建实例

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

如果是首次调用getInstance, 此时引用为null,就会进入if语句,从而创建实例 如果是后续再次调用getInstance,由于instance已经不再是null,所以直接返回创建好的引用 这样设定,仍然可以保证该类是唯一实例,与此同时,创建实例的时机就不是程序驱动时,而是第一次调用getInstance的时候了

 4.懒汉模式(多线程版)

上⾯的懒汉模式的实现是线程不安全的.   线程安全问题发⽣在⾸次创建实例时. 如果在多个线程中同时调⽤ getInstance ⽅法, 就可能导致创建 出多个实例. ⼀旦实例已经创建好了, 后⾯再多线程环境调⽤getInstance就不再有线程安全问题了(不再修改  instance 了)  而加上 synchronized 可以改善这⾥的线程安全问题.

代码语言:javascript
复制
class SingletonLazy {
    private static SingletonLazy instance = null;
    //这个指引指向唯一实例,想爱你初始化为空,而不是立即创建实例
    
    private static Object locker = new Object();
    public static SingletonLazy getInstance() {
        synchronized (locker) {
            if (instance == null) {
                instance = new SingletonLazy();
            }
        }
      
        return instance;
    }
    private SingletonLazy() {}
}

5.懒汉模式(多线程)(改进)

加锁 / 解锁是⼀件开销⽐较⾼的事情. ⽽懒汉模式的线程不安全只是发⽣在⾸次创建实例的时候. 因此后续使⽤的时候, 不必再进⾏加锁了. 所以这个时候可以在家一个 if 判定是否要加锁.

代码语言:javascript
复制
 private static Object locker = new Object();
    public static SingletonLazy getInstance() {
        if (instance == null) {  //第一层 if 是判定是否要加锁
            synchronized (locker) {
                if (instance == null) {// 第二层 if 是判定是否要创建对象
                    instance = new SingletonLazy();
                }
            }
        }
        return instance;
    }

 这个代码还是有些问题 指令重排序 , 是编译器优化的一种方式 , 也是引起线程安全的问题. 指令重排序是指调整原有的代码执行顺序,保证逻辑不变的前提下,提高程序效率

代码语言:javascript
复制
 instance = new SingletonLazy();

这行代码可以拆成三大步骤 1.申请一段内存空间 2.在这个内存上调用构造方法,创建出这个实例 3.把这个内存地址赋值给 instance 引用变量

 所以,解决上述问题 需要用到 volatile,它有两个功能: 1.保证内存可见性,每次访问变量必须都要重新读取内存,而不是直接优化到寄存器 / 缓存中 2.禁止指令重排序, volatile 修饰的变量的读写操作相关指令,不能被重排序的 所以最终代码为下

代码语言:javascript
复制
class SingletonLazy {
    private volatile static SingletonLazy instance = null;
    //这个指引指向唯一实例,想爱你初始化为空,而不是立即创建实例

    private static Object locker = new Object();
    public static SingletonLazy getInstance() {
        if (instance == null) {  //第一层 if 是判定是否要加锁
            synchronized (locker) {
                if (instance == null) {// 第二层 if 是判定是否要创建对象
                    instance = new SingletonLazy();
                }
            }
        }
        return instance;
    }
    private SingletonLazy() {}
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-07-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.什么是设计模式?
  • 2.饿汉模式
  • 3.懒汉模式
  •  4.懒汉模式(多线程版)
  • 5.懒汉模式(多线程)(改进)
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档