单例模式之懒汉式

【理论知识】单例模式(懒汉式+饿汉式+枚举) 软件设计模式(Software Design Pattern) 又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。它描述了在软件设计过程中的一些不断重复发生的问题,以及该问题的解决方案。也就是说,它是解决特定问题的一系列套路,是前辈们的代码设计经验的总结,具有一定的普遍性,可以反复使用。其目的是为了提高代码的可重用性、代码的可读性和代码的可靠性。... 阅读详情
AI摘要

文章详细讲解了Java中的懒汉式单例模式实现,包括其线程不安全问题、双重检查锁的解决方案以及volatile关键字的作用。同时,提到了反射和序列化对单例模式的挑战,最后推荐使用枚举来实现线程安全且防反射、序列化的单例模式。

       在上篇文章中,我们讲了单例模式中的饿汉式,今天接着来讲懒汉式。

1.懒汉式单例模式的实现

public class LazySingleton {

    private static LazySingleton instance = null;

    // 让构造函数为private,这样该类就不会被实例化
    private LazySingleton() {
    }

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

    public long getRamAddress() {
        return this.hashCode();
    }
}

      懒汉式的特点在于有需要时才实例化。

2.懒汉式线程安全

      在多线程的环境下,getInstance方法会导致线程不安全。因为在getInstance时,可能A、B两个线程几乎同时进入,在A实例化未完成的情况下,B判断实例仍然为null,因此继续实例化,这样有实例化了两个不同的对象,明显违背了单例的初衷。这里可以稍微验证一下线程安全问题,为了方便验证,在getInstance让当前线程sleep:

    public static LazySingleton getInstance() {
        if (instance == null) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            instance = new LazySingleton();
        }
        return instance;
    }

       测试代码:

      /**
     * 公共方法,在多线程环境下测试单例,避免重复编写测试代码
     * @param threadCount 线程数
     * @param func        函数,用于获取单例
     * @param <T>
     */
    public static <T> void singLetonMultiThread(int threadCount, Supplier<T> func) {
        ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
        IntStream.range(0, threadCount).forEach(i -> {
            executorService.submit(() -> {
                System.out.println(func.get());
            });
        });

        // 等线程全部执行完后关闭线程池
        executorService.shutdown();
        try {
            executorService.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void lazySingLetonTest() {
        Supplier func = () -> LazySingleton.getInstance();
        singLetonMultiThread(10, func);
    }

测试结果如下:

  

       十个线程里出现了8个不同的对象!

2.1 双重检查锁

      我们需要改进一下getInstance方法,考虑到效率问题,我们不想直接在getInstance方法上加锁,因为这种方式下每次调用getInstance()时都需要进行线程锁定判断,在多线程高并发访问环境中,将会导致系统性能大大降低。于是,我们得在判断实例是否为空时加锁,并且进行双重检查:

    public static LazySingleton getInstance() {
        if (instance == null) {
            synchronized (LazySingleton.class) {
                // 这里需要再判断一次,因为可能有其它线程已经创建实例
                if (instance == null) {
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }

       这样看起来似乎是解决了问题了。

2.2 指令重排

       但这还有瑕疵。对于:

instance = new LazySingleton();

       这个创建对象的语句其实是个非原子操作,在极端的多线程环境下,会存在安全问题。对象的创建过程,在执行的时候分解成以下三条指令:

memory=allocate(); 			//1.分配对象的内存空间
ctorInstance(memory);       //2.执行构造方法来初始化对象
instance=memory;			//3.设置instance指向刚分配的内存地址

       正常执行顺序应该是1->2->3,但可能指令会被重排序为1->3->2,也就是说,2、3步有可能发生指令重排导致重排序,因为synchronized只能保证有序性,但无法禁止指令重排。假设有两个线程A、B,在双重检查锁内,从cpu时间片上的执行顺序如下:

       A线程执行完3还没执行2,虽然分配了内存空间已,但是还没初始化对象,而此时B线程进来判断(instance == null),由于instance已经指向了内存空间,所以instance != null,于是直接返回了对象,但此时对象还未初始化。这样一来,线程B将得到一个还没有被初始化的对象。

       为了防止指令重排,需在声明instance对象时加上volatile关键词:

public class LazySingleton {

    // volatile,确保本条指令不会因编译器的优化而省略,且要求每次直接读值
    private static volatile LazySingleton instance = null;

    // 让构造函数为private,这样该类就不会被实例化
    private LazySingleton() {
        //System.out.println("懒汉式单例初始化!");
    }

    public static LazySingleton getInstance() {
        if (instance == null) {
            synchronized (LazySingleton.class) {
                // 这里需要再判断一次,因为可能有其它线程已经创建实例
                if (instance == null) {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }

    public long getRamAddress() {
        return this.hashCode();
    }
}

        volatile关键词具备以下功能:
       1. 避免编译器将变量缓存在寄存器里 
       2. 避免编译器调整代码执行的顺序 

       使用volatile声明的变量可以强制屏蔽编译器和JIT的优化工作,能够防止双重检查锁的指令重排。

3.反射破坏单例

      正如在上篇文章饿汉式单例中所说的那样,懒汉式同样有反射的问题。我们采用跟懒汉式一样的方法常识防止破坏单例:

public class LazySingleton {

    private static volatile LazySingleton instance = null;

    // 让构造函数为private,这样该类就不会被实例化
    private LazySingleton() {
        //System.out.println("懒汉式单例初始化!");
        synchronized (LazySingleton.class) {
            if(instance != null){
                throw new RuntimeException("单例构造器禁止反射调用");
            }
        }
    }

    public static LazySingleton getInstance() {
        if (instance == null) {
            synchronized (LazySingleton.class) {
                // 这里需要再判断一次,因为可能有其它线程已经创建实例
                if (instance == null) {
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }

    public long getRamAddress() {
        return this.hashCode();
    }
}

      再测试一下看看:

    public static void LazySingletonReflectionTest() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<LazySingleton> clazz = LazySingleton.class;
        // 获取HungrySingLeton的默认构造函数
        Constructor<LazySingleton> constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        // 调用默认构造函数创建实例
        LazySingleton h1 = constructor.newInstance();
        LazySingleton h2 = constructor.newInstance();
        System.out.println(h1.getRamAddress());
        System.out.println(h2.getRamAddress());
    }

      运行结果:

      没用!因为在类加载的时候,懒汉式单例根本就没有对单例进行初始化,然后反射通过构造函数获取单例,获取的对象都是不同的,所以没法防止反射破坏。当然,如果是先通过getInstance获取单例,再反射,这种情况下就可以防止反射,如:

    public static void reflectionTest2() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        LazySingleton h0 = LazySingleton.getInstance();
        Class<LazySingleton> clazz = (Class<LazySingleton>) h0.getClass();
        // 获取HungrySingLeton的默认构造函数
        Constructor<LazySingleton> constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        // 调用默认构造函数创建实例
        LazySingleton h1 = constructor.newInstance();
        System.out.println(h0.getRamAddress());
        System.out.println(h1.getRamAddress());
    }

      懒汉式单例对这个问题没有好的解决办法。

4.优雅的单例实现——枚举

      单例模式存在线程安全、反射、序列化漏洞的问题,虽然可以想办法解决,但代码也比较臃肿了。换个角度,可以用枚举来实现,去规避这些问题。

public enum Singleton {

    INSTANCE;

    public void doSomething() {
        System.out.println("这里实现自己的业务");
    }
}

4.1 枚举单例防止反射

       先测试一下枚举单例的反射:

    public static void enumSingletonRlectionTest() throws NoSuchMethodException,
            IllegalAccessException, InvocationTargetException, InstantiationException {
        Singleton instance1 = Singleton.INSTANCE;
        Constructor<Singleton> declaredConstructor = Singleton.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        //java.lang.NoSuchMethodException: com.kuang.single.EnumSingle.<init>()
        Singleton instance2 = declaredConstructor.newInstance();
        System.out.println(instance1);
        System.out.println(instance2);
    }

       结果抛出异常 ,没有构造函数Singleton.<init>():

       这并非防止了反射。枚举Enum是个抽象类,它有个构造函数:

protected Enum(String name, int ordinal) {
    this.name = name;
    this.ordinal = ordinal;
}

       我们定义了枚举类单例,实际上就是继承了Enum,所以会有(String.class,int.class)的构造器,测试方法改造下:

    public static void enumSingletonRlectionTest() throws NoSuchMethodException,
            IllegalAccessException, InvocationTargetException, InstantiationException {
        Singleton instance1 = Singleton.INSTANCE;
        Constructor<Singleton> declaredConstructor = Singleton.class.
                getDeclaredConstructor(String.class,int.class);
        declaredConstructor.setAccessible(true);
        //java.lang.NoSuchMethodException: com.kuang.single.EnumSingle.<init>()
        Singleton instance2 = declaredConstructor.newInstance();
        System.out.println(instance1);
        System.out.println(instance2);
    }

       结果如下:

       Cannot reflectively create enum objects(无法以反射方式创建枚举对象),可以防止反射,因为反射在通过构造函数的newInstance方法创建对象时,会检查该类是否ENUM修饰,如果是则抛出异常,反射失败。所以枚举类不能通过反射来创建对象

4.2 枚举单例避免序列化问题

       普通的Java类的反序列化过程中,会通过反射调用类的默认构造函数来初始化对象。所以,即使单例中构造函数是私有的,也会被反射给破坏掉。由于反序列化后的对象是重新new出来的,所以这就破坏了单例,必须新增readResolve方法才能防止破坏。

       但是,枚举的反序列化并不是通过反射实现的。在序列化时,Java仅仅是将枚举对象的name属性输出到结果中,反序列化时则通过java.lang.Enum的valueOf(String name) 方法,根据名字查找内存中是否已经有该对象,若找到了就会直接使用它,如果不存在就会抛出异常。同时。编译器不允许任何对这种序列化机制的定制,因此禁用了writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法。这样一来,序列化的方式就无法创建新的对象了,也就不会发生由于反序列化导致的单例破坏问题。

        实际测试一下:

    public static void enumSingletonSerializable() {
        ObjectOutputStream oos = null;
        ObjectInputStream ois = null;
        try {
            File file = new File("d:\\Singleton.txt");
            // -----------序列化-------------
            // 创建输出流
            oos = new ObjectOutputStream(new FileOutputStream(file));
            //将单例对象写到文件中  序列化
            oos.writeObject(Singleton.INSTANCE);
            oos.flush();

            // -----------反序列化-------------
            // 从文件读取单例对象
            ois = new ObjectInputStream(new FileInputStream(file));
            // 反序列化得到对象singLeton
            Singleton singLeton= (Singleton)ois.readObject();
            System.out.println(singLeton == Singleton.INSTANCE); //false
            file.deleteOnExit();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if(oos != null) {
                    oos.close();
                }
                if(ois != null) {
                    ois.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

     

       测试结果:

4.3 多线程安全

       当我们使用enmu来定义一个枚举类型的时候,编译器会自动帮我们创建一个final类型的类继承Enum类,所以枚举类型不能被继承;同时,编译器所创建的类中,属性和方法也都是都是static类型的,因为static类型的属性会在类被加载之后被初始化。当一个Java类第一次被真正使用到的时候静态资源被初始化、Java类的加载和初始化过程都是线程安全的(因为虚拟机在加载枚举的类的时候,会使用ClassLoader的loadClass方法,而这个方法使用同步代码块保证了线程安全)。所以,创建一个enum类型是线程安全的。所以,创建一个enum类型是线程安全的。

       基于前面的分析,单例类的线程安全问题,主要就是单例初始化过程中的线程安全问题。而由于枚举的以上特性,枚举实现的单例是天生线程安全的。所以用枚举实现的单例是最好的方式!

参考文章:

两种单例模式详解(内含懒汉式的双重校验锁详解)
为什么我墙裂建议大家使用枚举来实现单例

深度分析Java的枚举类型—-枚举的线程安全性及序列化问题

设计模式 - 单例模式(饿汉,懒汉),就该这样学! 是一种给常见的设计模式,先来他谈谈何为设计模式,在代码领域里,很多程序员的水平参差不齐,于是就有大佬们根据一些常见的需求,整理出来的一些应对办法;那么单例就是指单个实例(对象),也就是说一个类只能有一个实例,例如,在中国,一个男人只能娶一个老婆是一个道理;单例模式,本质上就是借助变成语言的语法特性,强制限制某个类,不能创建多个实例。在Java中有些东西是天然的单例,例如static,他可以修饰成员/属性,也就是我们口中熟知的类成员/类属性,实际上这种叫法,也是有一定原因的,也就是这个类特有的成员和属性。 阅读详情

相关推荐

Proteus仿真实战:用STM32F103C8T6输入捕获模式精准测量PWM频率(附完整代码)

本文详细解析了如何在Proteus仿真环境中,利用STM32F103C8T6的输入捕获模式精准测量PWM频率。文章从输入捕获的工作原理、Proteus电路搭建、模块化代码实现到调试优化,提供了完整的实战指南和代码示例,帮助开发者深入理解并掌握这一关键技术。

weixin_29158047的博客 257

单例模式之懒汉式单例模式

单例模式之懒汉式单例模式 本文将探讨三种懒汉式单例模式,会从代码的运行效率,线程安全等方面探讨。 在上一篇文章中,刚刚介绍饿汉式单例模式,在本文开头先简单的说一下懒汉式单例模式和饿汉式单例模式的区别: 首先懒汉式单例模式,是在程序需要用到该实例时才会创建的。这样就不会像饿汉式一样,在类加载过程中就会创建。这样就减少了内存的浪费,当然也自然而然的带来了一些缺点:需要时才创建,影响程序的执行效率。另外,懒汉式单例模式在使用时进行创建,势必会带来线程安全的问题,需要解决以上问题,就需要用到锁。锁的使用也在一定程度

jl19960421的博客 5500

单相PWM整流器搭建训练——(一)拓扑原理介绍

单相整流器是一种将交流电转换为直流电的设备,主要由变压器、整流元件和滤波电路组成。变压器将输入的交流电压转换为适合整流的较低电压,整流元件(如二极管)利用其单向导电性将交流电转换为脉动直流电,最后滤波电路去除脉动直流电中的纹波,输出较为平滑的直流电,从而实现交流到直流的转换,广泛应用于各类电源设备中。

DikouNass的博客 1587

单例模式(饿汉式、懒汉式)详解

本文章详细介绍了单例模式,设计模式中创建者模式的一种,单例模式包括饿汉式和懒汉式两种实现方式,通过代码实现单例模式,还介绍通过反序列化和反射破坏单例吗哦是。

zlc0827的博客 1万+

stm32f103运行FreeRTOS使用DMA实现USART1不定长接收

使用DMA是实现USART1的不定长接收和发送数据。接收采用FIFO缓存和队列的方式向应用层抛数据。首先定义接收和发送的缓存区,缓存区的大小根据收发数据量确定。DMA接收开启转移完成和转移一般中断。在中断中从缓存向FIFO转移数据。串口开启空闲中断,实现接收数据的分帧。分帧后的数据节点通过写入到队列。发送数据缓存的大小根据发送数据量调整。然后初始化串口和DMA。串口的发送数据接口函数。...

polaris_zgx的博客 1991

sap 标准委外和工序委外_SAP PP 工序委外详解

在SAP里有外包和外协两种:外包:指把原材料发出去,扣减库存,然后供应商加工生产,最后再作为成品入库。主要是在MM模块下实现的。对应的英语叫Subcontracting。外协:一般是指某种工序外协,在工艺路线里设置某个工序为外协后,系统自动会产生一个采购订单,一般是指采购某种服务。采购回来后再继续本公司的其他工序。主要是在PP模块下实现的。对应的英语叫Processing。2.当有委外加工的业务流...

weixin_26849971的博客 4579

设计模式—单例模式(饿汉式、懒汉式)

相信大家在面试过程中被提到单例模式的次数不少。单例模式只是众多程序设计模式中最常用的一种,其它的还有工厂模式、建造模式、策略模式等。单例模式,指的是一个类中的对象只能有一个,它在内存中只会创建一次对象的设计模式。在程序中如果多次用到同一个类中的方法进行操作时,在使用时就会创建多个对象。为了防止频繁创建对象造成内存资源浪费,就可以使用单例模式。可以让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一单例对象。

晓 铭的博客 5180

微信小程序 - 最新图片组拖拽排序更换位置功能,类似微信朋友圈九宫格图片的拖动排序效果,微信小程序实现九宫格图片拖拽调换位置,小程序详细实现图像拖曳拖动切换位置排序功能(详细示例代码,一键复制开箱即用

微信小程序,wechat,小程序,图片排序,图像排序,九宫格拖拽排序,拖曳,拖动,拖拽,仿微信朋友圈,朋友圈宫格图片排序,微信朋友圈图片拖动排序效果,实现九宫格图片拖拽,图像拖拽排序组件插件,小程序实现拖拽图片切换图片位置,图片拖动互换位置,图像拖拽切换位置,九宫格图片组合拖动排序,交换九宫格图片的位置,小程序上传图片后,如何实现拖拽排序功能,小程序怎么实现图片拖拽排序,微信小程序图片上传,九宫格拖拽组件,选择图片并拖动_微信小程序图片拖拽,微信小程序长按拖拽排序,图像的拖拽移动,多图拖拽排序功能,实现九宫

✅ 精选推荐文章 4454

懒汉式单例模式

懒汉式单例适合于需要延迟加载且实例化成本较高的场景(如日志管理器、配置加载器等)。在并发场景下,最好使用线程安全的实现,例如同步方法版或双重检查锁定版,以确保唯一实例的正确性和性能的平衡。指令重排序:编译器和处理器在执行程序时可能会对指令进行重排序,以优化性能。volatile 关键字会禁止这种重排序,确保变量的初始化和其他操作的执行顺序符合程序的预期。这对于实现线程安全的懒汉式单例模式非常重要,因为它保证了对象在初始化完成后才会被其他线程看到。

qq_35842623的博客 956

豆包手机为什么能自己“玩手机”?原理是什么?豆包手机系统运行机制深度解析:读屏、抓取、推理链路全拆解

作为一个每天被前端 Bug 打醒的程序员,我这一阵子在关注“豆包手机”这个关键词,刷到的相关视频简直铺天盖地。朋友们,说句实话,我一开始也只是抱着“看看热闹”的心态,最近一位博主下面的内容,不是“它能做什么”,而是。如果你之前看过很多评测,这篇文章会给你完全不一样的视角。

代码简单说 Vue、JAVA、PHP、Node.js 熟练运用,接口、架构、性能全搞定。 1015

86、高斯过程:学习、应用与自动相关性确定

本文介绍了高斯过程回归的基本原理及其在实际应用中的关键技术,重点探讨了通过最大化对数似然函数来学习模型超参数的方法,并详细阐述了自动相关性确定(ARD)技术如何用于评估输入变量的重要性。文章涵盖了从协方差函数定义、梯度计算到优化算法的应用流程,并通过流程图和表格形式总结了核心步骤与公式。此外,还讨论了贝叶斯处理方式及异方差问题的建模策略,展示了高斯过程在复杂数据建模中的灵活性与强大能力。

android的专栏 114

Java设计模式——懒汉式单例模式

一、懒汉式单例模式 懒汉式单例模式在被外部类调用时创建实例,因类加载速度快,但运行时获取对象的速度慢 二、实例 1、懒汉式1(线程不安全) public class LazySingleton{ private static LazySingleton instance; private LazySingleton(){} public st...

奔跑8蜗牛 2507

上位机 寻边器 通讯实战

寻边器通讯接口实现 本文介绍了上位机与盖泽(Gazer)和JEL品牌寻边器的通讯接口实现方案。主要内容包括: 接口定义:创建了IAlignerOperation接口,定义了获取状态、控制运动、真空吸附等核心功能。 具体实现: 盖泽寻边器(GazerAlignerCjh):通过串口通讯,实现了回零、校准、移动位置等操作 JEL寻边器(JelAligner):同样基于串口通讯,包含错误处理、状态查询等功能 工厂模式:通过AlignerFactory工厂类根据设备品牌创建对应的实现对象。 设备加载:展示了如何在系

StevenChen的博客 377

单例模式饿汉式与懒汉式详解

单例模式:顾名思义就是保证一个类仅有一个实例,并提供一个访问它的全局访问点。

weixin_42437102的博客 6907

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

单例模式中的懒汉式

懒汉式,什么时候使用对象,什么时候创建对象

a16696747377的博客 1692

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

【Java设计模式】之 单例模式(Singleton Pattern)模式 饿汉式单例模式 vs 懒汉式单例模式

什么是设计模式? 设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模免去我们自己再思考和摸索,设计模式就像是经典的棋谱,不同的棋局,我们用不同的棋谱,"套路”。 设计模式的分类: ① 创建型模式(5种):工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。 ② 结构型模式(7种):适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。 ③ 行为型模式(11种):策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、

weixin_46081857的博客 418

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

单例模式----懒汉式单例模式

单例模式有饿汉时模式和懒汉式;单例模式也就是说同一类只返回一个对象供外部类使用;懒汉式即延迟初始化单例。在多线程环境下,简单的懒汉式会有线程安全。 懒汉式单例模式解决线性安全问题如下: (1) 使用双重检查锁机制解决线程安全问题。 (2) 单例模式还有更好的解决方案,即使用静态类方式。 懒汉式单例模式典型代码: public class SingleTonDemo { /** * 单例模式---...

Sun_0117的博客 837

单例模式之懒汉式和饿汉式

所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类,并且该类只提供-一个取得其对象实例的方法。如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造器的访问权限设置为,这样,就不能用new操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的。(1)懒汉式:在需要的时候才创建 (2)饿汉

rqt1013_的博客 1977

懒汉式单例模式、饿汉式单例模式 以及 解决如何懒汉式单例模式线程安全问题

单例模式 文章目录单例模式1. 定义2. 如何使用单例模式3. 单例模式的实现3.1 饿汉式3.2 懒汉式4. 线程安全问题5. 解决懒汉式线程安全问题 1. 定义 什么是单例模式呢? 我们引用一下维基百科: ​ 单例模式,也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其

小白猿同学 776

Java的懒汉式双检锁单例模式

首先回忆一下,Java中的单例模式有两种,俗称“饿汉式”和“懒汉式”。懒汉式的线程问题可以通过加同步锁来解决,但是上面的懒汉式的代码的性能有点不好,同步锁加在了方法上面,如果实例之前已经初始化完成,每次调用方法都会去访问到同步锁,性能开销比较大,这个时候实际上不在需要加锁了,直接把实例化好的对象返回就可以了。这就引出了下面要说的双检锁,它其实是对懒汉式的一个优化。

weixin_48178004的博客 976

懒汉式单例模式和饿汉式单例模式的详解

1. //懒汉式单例模式   2. public class MySingleton {   3.    4.     //设立静态变量   5.     private static MySingleton mySingleton = null;   6.    7.     private MySingleton(){   8.         //私有化构造函数   9.  

wan23333的博客 687

单例模式——饿汉式与懒汉式

单例模式——饿汉式与懒汉式 单例模式是一种设计模式,即该类实例化后实例对象永远只有一个称为单例。 这里介绍两种实现方式,饿汉式与懒汉式。其中饿汉式是线程安全的,懒汉式线程不安全需要加锁。 两种方式的实现结果分别如下 先看饿汉式 /** * 单例设计模式,饿汉式 */ public class Singleton { private Singleton(){ } private static Singleton singleton = new Singleton();

liuliusix的博客 328

懒汉单例模式 java_Java懒汉式单例模式详解

标签:单例模式最常见的有两种,饿汉式和懒汉式两种。本文先简单说一下懒汉式单例,再着重叙述饿汉式单例,饿汉式单例是本文的叙述重点。懒汉式的优点是:写起来比较简单,而且不存在多线程同步问题,避免了synchronized所造成的性能问题;缺点是:初始化类的时候就需要构造实例,(即便你还没有用到这个实例),因此在某些特定条件下会耗费内存。懒汉式的写法如下:1 /**2 * 懒汉式单例3 *@author...

weixin_42540248的博客 690

单例模式懒汉式

单例模式 懒汉式:用的时候才创建对象 public class Teacher { private Teacher(){} private static Teacher t=null; //调用方法的时候再new Teacher(); //懒汉式有线程安全问题 //要加synchronized public synchronized stat...

qq_45491681的博客 171

懒汉式单例模式详解

懒汉式单例模式是一种在需要时才创建实例对象的单例模式,可以节省内存资源。但在多线程环境下需要注意线程安全问题,可以使用同步方法或双重检测锁机制等线程安全的实现方式。与饿汉式单例模式不同,懒汉式在类被加载时不创建实例对象,而是在首次使用该实例时才会创建。这种方式可以节省内存资源,因为实例对象只有在需要时才会被创建。懒汉式单例模式的实现方式有多种,包括基础的懒汉式、线程安全的懒汉式(如使用同步方法或双重检测锁机制)等。为了解决这个问题,可以使用同步方法或双重检测锁机制等线程安全的实现方式。

Sunal_Sue的博客 507
上一篇: 单例模式之饿汉式
下一篇: Java并发编程实践学习笔记(三)——共享对象之可见性
叶大师2018
博客等级 码龄19年 21粉丝 49原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值