当前位置: 首页 > news >正文

单例模式(线程案例)

单例模式可以分为两种:1.饿汉模式   2.懒汉模式


一.饿汉模式    

//饿汉模式👇
class MySingleTon{

    //因为这是一个静态成员变量,在类加载的时候,就创建了
    private static MySingleTon mySingleTon = new MySingleTon();
    
    //创建一个静态方法,方便在类外获取对象
    public static MySingleTon getMySingleTon() {
        return mySingleTon;
    }

    //创建一个私有的构造方法
    //避免通过new关键字来调用构造方法,进而创建实例
    //保证了唯一性
    private MySingleTon(){

    }
    
}

public class ThreadDemo8 {
    public static void main(String[] args) {
        MySingleTon s1 = MySingleTon.getMySingleTon();
        MySingleTon s2 = MySingleTon.getMySingleTon();
        System.out.println(s1 == s2);

    }
    
}

程序运行的结果

结果是true,说明了这两次都是获取了同一个实例!,这也说明成功实现了单例模式


因为这个对象是在类加载的时候创建,类加载是单线程的,所以线程安全。但是如果在代码中,没有使用对象,就会造成资源浪费!


二.懒汉模式

这是实现懒汉模式的代码👇

class MySingleTonLazy{
    private static MySingleTonLazy mySingleTonLazy = null;

    
    public static MySingleTonLazy getMySingleTonLazy() {
        if(mySingleTonLazy != null){
            mySingleTonLazy = new MySingleTonLazy();
        }
        return mySingleTonLazy;
    }

    
    //私有的构造方法
    private MySingleTonLazy(){


    }
}

public class ThreadDemo9 {
    public static void main(String[] args) {
        MySingleTonLazy s1 = MySingleTonLazy.getMySingleTonLazy();
        MySingleTonLazy s2 = MySingleTonLazy.getMySingleTonLazy();
        System.out.println(s1==s2);
    }
}

这个代码是有两个问题的:1.线程安全问题。2.指令重排序问题

1.线程安全问题

所以说,需要对其加锁。把这些指令打包成一个原子(整体),这样就不会出现这种情况了。


这是修改后的代码,是线程安全的!

第一个if是用来判断这个实例是否已经创建了,如果创建就直接跳过这个代码了。👆


2.指令重排序问题

mySingleTonLazy = new MySingleTonLazy()

这行代码在执行的时候,会分为3个步骤:

1.分配内存空间

2.初始化对象

3.将引用指向对象

这3个步骤可以是按132顺序执行,也可以是123顺序执行👇

如果是按132来执行的,先执行完1,然后执行3 ,在执行完3后,另一个线程也来执行,因为已经创建了对象,在if判断完后,直接跳过去了,但此时这个对象没有初始化,故而会有空指针问题


那么我们该如何解决这个问题👇

引入volatile关键字

1.防止内存可见性问题

2.解决指令重排序问题

此时这个volatile 解决指令重排序问题

 private volatile static MySingleTonLazy mySingleTonLazy = null;

那么这懒汉模式正确代码如下👇

class MySingleTonLazy {
    // 使用 volatile 修饰,保证可见性和禁止指令重排序
    private volatile static MySingleTonLazy mySingleTonLazy = null;
    private static Object locker = new Object();

    public static MySingleTonLazy getMySingleTonLazy() {
        // 第一次检查,判断实例是否已经创建
        if (mySingleTonLazy == null) {
            // 加锁,保证线程安全
            synchronized (locker) {
                // 第二次检查,防止多个线程同时通过第一次检查
                if (mySingleTonLazy == null) {
                    mySingleTonLazy = new MySingleTonLazy();
                }
            }
        }
        return mySingleTonLazy;
    }

    // 私有的构造方法,防止外部实例化
    private MySingleTonLazy() {

    }
}

public class ThreadDemo9 {
    public static void main(String[] args) {
        MySingleTonLazy s1 = MySingleTonLazy.getMySingleTonLazy();
        MySingleTonLazy s2 = MySingleTonLazy.getMySingleTonLazy();
        System.out.println(s1 == s2);
    }
}

总结:

线程安全:

饿汉模式:天生都线程安全,不用如何处理

懒汉模式:线程不安全,需要自己处理

资源利用率:

饿汉模式:那么创建出了这个实例,然后并没有使用这个实例,这就造成了资源浪费的情况

懒汉模式:用的时候,创建实例,避免不必要的资源浪费的情况

相关文章:

  • 失去的讨论区
  • PySide(PyQT)的信号槽框架的一个demo练习
  • Spring Boot 简介与快速搭建及启动流程详解
  • 剑指 Offer II 041. 滑动窗口的平均值
  • 用pytest进行单元测试(1)
  • SpringBoot @ConfigurationProperties 注解使用
  • 2025.3.3
  • 聊聊学习之数据-信息-知识-洞见-智慧-影响力
  • 【PTA】1019 数字黑洞
  • 绘制思维导图画布选型
  • 【OMCI实践】omci.lua脚本文件(独家分享)
  • C# IComparer<T> 使用详解
  • Web自动化中多浏览器并发
  • IO的概念和标准IO函数
  • 用Python+Flask打造可视化武侠人物关系图生成器:从零到一的实战全记录
  • 实训任务1.3 使用eNSP搭建基础网络
  • 如何解析API返回的JSON数据?
  • c++上课题目
  • GEE:计算长时间序列NPP与NDVI之间的相关系数
  • Vue3 TransitionGroup组件深入解析:结合Element Plus实践指南
  • 国台办:“台独”是绝路,外人靠不住
  • “铁血防守”制造8年最快丢球,恐惧中的阿森纳什么也做不了
  • 中青旅:第一季度营业收入约20.54亿元,乌镇景区接待游客数量同比减少6.7%
  • 民营经济促进法出台,自今年5月20日起施行
  • 解放日报:持续拿出排头兵姿态先行者担当
  • 中国建设银行浙江省分行原党委书记、行长高强接受审查调查