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

单例模式(线程案例)

单例模式可以分为两种: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);
    }
}

总结:

线程安全:

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

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

资源利用率:

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

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


http://www.kler.cn/a/570606.html

相关文章:

  • 使用Java操作Excel
  • R语言基础| 基本图形绘制
  • 数据集/API 笔记:新加坡风速 API
  • Linux12-UDP\
  • 打造高清3D虚拟世界|零基础学习Unity HDRP高清渲染管线(第一天)
  • go语言逆向-符号恢复
  • iOS逆向工程概述与学习路线图
  • Mysql面试篇笔记:
  • 【Python】在Windows下配置Python最小环境并在React执行Python脚本
  • SpringCloud系列教程(十一):token验证
  • unidbg 实现 JNI 与 Java 交互
  • ffmpeg源码编译支持cuda
  • PyCharm 对接 DeepSeek 大模型的详细操作流程
  • ArcGIS操作:11 计算shp矢量面的质心坐标
  • 【JAVA】ThreadPoolTaskExecutor 线程池学习、后端异步、高并发处理
  • ArcGIS操作:08 计算shp面积并添加到属性表
  • 解释 CSS 盒模型的概念以及如何使用 box-sizing 属性
  • 厦大团队|报告:《读懂大模型概念、技术与应用实践》140 页 PPT(文末附链接下载)
  • 知识图谱的推荐实现方案(Vue)
  • 泛型边界的使用