iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > JAVA >24种设计模式之单例模式(饿汉式、懒汉式)
  • 138
分享到

24种设计模式之单例模式(饿汉式、懒汉式)

javaPoweredby金山文档 2023-09-23 14:09:30 138人浏览 泡泡鱼
摘要

一、单例模式 单例模式( Singleton Pattern )是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。单例模式是创建型模式。单例模式在现实生活中应用也非常广泛,例如,总统,班主任等。J2EE标准中的Servl

一、单例模式

单例模式( Singleton Pattern )是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。单例模式是创建型模式。单例模式在现实生活中应用也非常广泛,例如,总统,班主任等。J2EE标准中的ServletContext 、ServletContextConfig 等、spring框架应用中的。

  • 特点:构造方法私有,提供一个全局访问点。

  • 实现方式:有很多,1.饿汉式 2.懒汉式 3.注册式 4.ThreadLocal

  • 优点:内存中只有一个实例,减少内存开销;避免对资源多重占用;设置全局访问点,严格控制访问。

  • 缺点:没有接口,扩展困难;如果要扩展单例对象,只有修改代码,没有其他途径,不符合程序的开闭原则。

二、饿汉式单例模式

饿汉式单例模式在类加载的时候就立即初始化,并且创建单例对象。它绝对线程安全,在线程还没出现以前就实例化了,不可能存在访问安全问题。

总结

final:防止反射破坏单例。

饿汉式缺点:可能会造成内存空间的浪费。

饿汉式单例模式适用于单例对象较少的情况。这样写可以保证绝对线程安全、执行效率比较高。但是它的缺点也很明显,就是所有对象类加载的时候就实例化。这样一来,如果系统中有大批量的单例对象存在,那系统初始化是就会导致大量的内存浪费。

饿汉式之单例实现方式

1、标准饿汉模式

通过私有构造器,防止外部进行实例创建;通过属性在类加载时实例化对象,提供全局访问方法取得实例。利用代码的执行先后顺序,在线程还没有出现前就完成了实例化。

public class HungrySingleton {    // 静态实例代码段,饿汉实现类加载初始化时调用构造方法    private static final HungrySingleton hungrySingleton = new HungrySingleton();         // 私有方法防止外部调用创建对象    private HungrySingleton() {}    // 外部类获得单例对象方法    public static HungrySingleton getInstance() {        return hungrySingleton;    }}

该单例实现方式可以被反序列化和反射破坏:

(1)反射破坏方式如下:该方式可以通过构造方法创建出一个全新的实例对象。

public static void reflect() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {        System.out.println(Test.getInstance());        //反射破坏        //得到类        Class c = Test.class;        Constructor constructor = c.getDeclaredConstructor();        //设置私有可调用        constructor.setAccessible(true);        // 打印创建的实例对象        System.out.println(constructor.newInstance());    }

可见该方法是通过调用构造方法创建出一个新的对象。

(2)反序列化破坏单例方式如下:

public static void ser() throws IOException, ClassNotFoundException {        //反序列化        ByteArrayOutputStream outputStream=new ByteArrayOutputStream();        ObjectOutputStream objectOutputStream=new ObjectOutputStream(outputStream);        //将类转化        objectOutputStream.writeObject(Test.getInstance());        System.out.println(Test.getInstance());        ObjectInputStream objectInputStream=new ObjectInputStream(new ByteArrayInputStream(outputStream.toByteArray()));        //读出类,变为一个新的类        Test test= (Test) objectInputStream.readObject();        System.out.println(test);    }

该方式可以看出反序列化构造出的对象并不是通过构造方法。

由此针对上面两种破坏方式做出优化得到以下的代码:

public class Test implements Serializable {    //静态实例代码段,饿汉实现类加载初始化时调用构造方法    private static Test Instance=new Test();    //私有方法防止外部调用创建对象    private Test(){        if(Instance!=null)// 此处方式反射调用破环单例对象,抛出异常            throw new RuntimeException("单例模式不能创建");        System.out.println("构造方法");    }    //外部类获得单例对象方法    public static Test getInstance(){        return Instance;    }    //其他方法    public static void otherMethod(){        System.out.println("other");    }    //防止反序列化破坏单例    public Object readResolve(){        return  Instance;    }}
2、静态代码块机制
public class HungryStaticSingleton {    // 静态志方式饿汉式单例    private static final HungryStaticSingleton hungrySingleton ;    static {        hungrySingleton = new HungryStaticSingleton();    }             private HungryStaticSingleton() {}    //取实例方法    public static HungryStaticSingleton getInstance() {        return hungrySingleton;    }}
3、枚举类实现饿汉:枚举类实现方式不会被反射和反序列化破环单例
public enum  Test_1 {    Instance;    //枚举类默认构造方法私有    Test_1(){        System.out.println("构造方法");    }    //获取对象    public static Test_1 getInstance(){        return Instance;    }    //其他方法    public static void otherMethod(){        System.out.println("other");    }}

三、懒汉式单例模式

懒汉式类被加载的时候,没有立刻被实例化,第一次调用getInstance的时候,才真正的实例化。

如果要是代码一整场都没有调用getInstance,此时实例化的过程也就被省略掉了,又称“延时加载”

一般认为“懒汉模式” 比 “饿汉模式”效率更高。

懒汉模式有很大的可能是“实例用不到”,此时就节省了实例化的开销。

懒汉式之单例实现方式

1、普通的懒汉式
public class LazySingleton {    private LazySingleton() {    }    private volatile static LazySingleton instance;    //加入了同步代码,解决线程不安全问题    public synchronized static LazySingleton getInstance() {        if (instance == null) {            instance = new LazySingleton();        }        return instance;    }}

这种设计明显的一个问题就是执行效率低,无论是否已经存在实例,在多线程的情况下都会发生阻塞。

对以上代码进行改进,首先让当程序中实例存在的时候,直接返回实例,不需要抢占。当程序中不存在实例时,再抢占锁进行创建。根据以上的思想,出现了第二种懒汉式方式:

2、双重检查锁DCL(Double Check Lock双端检锁)
public class LazyDoubleCheckSingleton {    private LazyDoubleCheckSingleton() {    }    private volatile static LazyDoubleCheckSingleton instance;    public static LazyDoubleCheckSingleton getInstance() {        //确定是否需要阻塞        if (instance == null) {            // 线程安全:双重检查锁(同步代码块)            synchronized (LazyDoubleCheckSingleton.class) {                //确定是否需要创建实例                if (instance == null) {                    //这里在多线程的情况下会出现指令重排的问题,所以对共有资源instance使用关键字volatile修饰                    instance = new LazyDoubleCheckSingleton();                }            }        }        return instance;    }}

对于第二种方式,较第一种方式而言,性能提高了,但是代码的可读性差了。


DCL(Double Check Lock双端检锁)机制不一定线程安全,原因是有指令重排序的存在,加入volatile可以禁止指令重排。

原因在于某一个线程执行到第一次检测,读取到的instance不为null时,instance的引用对象可能没有完成初始化

instance = new LazyDoubleCheckSingleton();可以分为以下3步完成(伪代码)

memory = allocate(); // 1.分配对象内存空间

instance(memory); // 2.初始化对象

instance=memory; // 3.设置instance指向刚分配的内存地址,此时instance != null

步骤2和步骤3不存在数据依赖关系,而且无论重排前还是重排后程序的执行结果在单线程中并没有改变,因此这种重排优化是允许的。

memory = allocate(); // 1.分配对象内存空间

instance=memory; // 3.设置instance指向刚分配的内存地址,此时instance != null,但是对象还没有初始化完成

instance(memory); // 2.初始化对象

但是指令重排只会保证串行语义的执行的一致性(单线程),但并不会关心多线程间的语义一致性。

所以当一条线程访问instance不为null时,由于instance实例未必已初始化完成,也就造成了线程安全问题。

3、静态内部类
 public class LazyInnerClassSingleton {    //虽然构造方法私有了,但是逃不过反射的法眼    private LazyInnerClassSingleton(){};     // 懒汉式单例    // LazyHoler里面的逻辑需等外部方法调用时候才执行    // 巧妙运用了内部类的特性    // JVM底层逻辑,完美避免了线程安全问题    public static final LazyInnerClassSingleton getInstance(){        return LazyHoler.LAZY;    }     public static class LazyHoler{        private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();    }}

为防止调用者反射破坏,可以这么写:

public class LazyInnerClassSingleton {    //虽然构造方法私有了,但是逃不过反射的法眼    private LazyInnerClassSingleton(){        // 防止调用者反射攻击;         if(LazyHoler.LAZY != null){            throw new RuntimeException("禁止创建多个实例!"); // 其他写法也可加上        }    };     // 懒汉式单例    // LazyHoler里面的逻辑需等外部方法调用时候才执行    // 巧妙运用了内部类的特性    // JVM底层逻辑,完美避免了线程安全问题    public static final LazyInnerClassSingleton getInstance(){        return LazyHoler.LAZY;    }     public static class LazyHoler{        private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();    }}

分析:静态内部类相对来说更优,LazyHoler里面的逻辑需等外部方法调用时候才执行,所以也属于懒汉式,巧妙运用了内部类的特性,JVM底层逻辑,完美避免了线程安全问题,

来源地址:https://blog.csdn.net/inexaustible/article/details/128816512

--结束END--

本文标题: 24种设计模式之单例模式(饿汉式、懒汉式)

本文链接: https://www.lsjlt.com/news/416504.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 24种设计模式之单例模式(饿汉式、懒汉式)
    一、单例模式 单例模式( Singleton Pattern )是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。单例模式是创建型模式。单例模式在现实生活中应用也非常广泛,例如,总统,班主任等。J2EE标准中的Servl...
    99+
    2023-09-23
    java Powered by 金山文档
  • 设计模式之单例模式(懒汉, 饿汉)
    文章目录 一. 单例模式概述二. 单例模式的实现1. 饿汉模式2. 懒汉模式 一. 单例模式概述 单例模式是一种常用的软件设计模式, 该模式的主要目的是确保某一个类在内存中只能有一个实...
    99+
    2023-09-05
    单例模式 设计模式 java 多线程 线程安全
  • Java设计模式之单例模式实例详解【懒汉式与饿汉式】
    本文实例讲述了Java设计模式之单例模式。分享给大家供大家参考,具体如下:单例模式就是产生一个对象实例,供外外部访问。它的应用场景就是在这个类在全局真资源需要统一访问,否则会造成混乱时,才有必要设计成单例。懒汉式,就是在使用这个对象时,才去...
    99+
    2023-05-31
    java 设计模式 单例模式
  • java 单例模式(懒汉式与饿汉式)
    java 单例模式单例模式是一种常用的软件设计模式。在它的可信结构中只包含一个被实例化单例的特殊类。通过单例设计模式可以把整系统中的一个类只有一个实例。单例设计模式又分为两种方式,懒汉式和饿汉式。 (1)懒汉式,就是只有当调用getInst...
    99+
    2023-05-31
    java 单例模式 ava
  • C++单例模式的懒汉模式和饿汉模式详解
    目录懒汉模式饿汉模式线程安全的懒汉模式总结懒汉模式 懒汉模式在第一次用到类实例的时候才会去实例化,就是不到调用getInstance函数时,这个类的对象是一直不存在的。懒汉本身是线程...
    99+
    2024-04-02
  • C++单例模式的懒汉模式和饿汉模式怎么实现
    本文小编为大家详细介绍“C++单例模式的懒汉模式和饿汉模式怎么实现”,内容详细,步骤清晰,细节处理妥当,希望这篇“C++单例模式的懒汉模式和饿汉模式怎么实现”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。懒汉模式懒...
    99+
    2023-06-29
  • 详解Java单例模式中的饿汉和懒汉模式
    目录一.什么是单例模式一.饿汉模式1.饿汉模式的概念2.饿汉模式代码3.多线程是否线程安全二.懒汉模式1.懒汉模式的概念2.单线程情况下的懒汉模式3.多线程情况下的懒汉模式一.什么是...
    99+
    2023-05-14
    Java单例模式 Java单例饿汉模式 Java单例懒汉模式
  • java 中单例模式饿汉式与懒汉式的对比
    java 中单例模式饿汉式与懒汉式的对比概念:保证一个类仅有一个实例,并提供一个访问它的全局访问点。以前我们的做法是设置一个全局变量,也就是让它使得一个对象被访问。但是它不能防止你实例多个对象。这时我们可以让类自身负责保存它的唯一实例,这个...
    99+
    2023-05-31
    java 单例模式 中单
  • Java单例模式中的饿汉和懒汉模式怎么实现
    今天小编给大家分享一下Java单例模式中的饿汉和懒汉模式怎么实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。什么是单例模式...
    99+
    2023-07-05
  • Java多线程案例之单例模式懒汉+饿汉+枚举
    目录前言:1.单例模式概述2.单例模式的简单实现2.1饿汉模式2.2懒汉模式2.3枚举实现单例模式前言: 本篇文章将介绍Java多线程中的几个典型案例之单例模式,所谓单例模式,就是一...
    99+
    2024-04-02
  • Java单例模式的线程安全,饿汉和懒汉模式详解
    单例模式 创建唯一的一个变量(对象),在类中将构造函数设为protected或者private(析构函数设为相对应的访问权限),故外部不能实例化对象,再提供访问它的一个全局访问点,即...
    99+
    2024-04-02
  • 一文详解Java的饿汉和懒汉设计模式
    目录饿汉设计模式懒汉设计模式饿汉和懒汉模式的区别本文主要讲述java的饿汉和懒汉设计模式 饿汉和懒汉设计模式的目的:使得该类的对象,只能有一个,不允许其他类,创建该类的对象。 饿汉设...
    99+
    2022-12-20
    Java饿汉 懒汉 设计模式 Java饿汉设计模式 Java 懒汉设计模式 Java 设计模式
  • Java的懒汉与饿汉模式怎么实现
    今天小编给大家分享一下Java的懒汉与饿汉模式怎么实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。首先,我们先了解下单例模...
    99+
    2023-06-27
  • java中懒汉和饿汉模式有什么不同
    这期内容当中小编将会给大家带来有关java中懒汉和饿汉模式有什么不同,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。Java的优点是什么1. 简单,只需理解基本的概念,就可以编写适合于各种情况的应用程序;2...
    99+
    2023-06-14
  • java懒汉式和饿汉式有什么不同
    懒汉式和饿汉式都是单例设计模式的实现方式,但它们有以下不同:1. 初始化时间:饿汉式是在类加载的时候就创建单例对象,而懒汉式是在第一...
    99+
    2023-08-30
    java
  • Java设计模式之单例模式
    目录什么是设计模式?单例模式是什么?单例模式设计的原则是什么?Java实现单例模式的5种方式?懒汉饿汉静态内部类双重校验锁DCL(Double Check Lock)枚举(num)总...
    99+
    2024-04-02
  • python设计模式之单例模式
    单例模式是一种创建型设计模式,它确保一个类有且只有一个特定类型的对象,并提供全局访问点。其意图为: 确保类有且只有一个对象被创建 为对象提供一个访问点,使程序可以全局访问该对象 控制共享资源的并行访问 简单理解:单例...
    99+
    2023-01-30
    模式 python
  • C#设计模式之单例模式
    单例模式也是创建型模式的一种,也是23种设计模式中比较简单的一种。见名思意,在整个软件系统中,只有某个类型的一个对象,并且访问他的地方也只有一个,也就是只有一个全局对象访问点,这个实...
    99+
    2024-04-02
  • JavaScript设计模式之单例模式
    目录单例模式实现单例模式透明的单例模式用代理实现单例模式惰性单例通用的惰性单例小结单例模式 单例模式是一种常用的模式,有一些对象我们往往只需要一个,比如线程池、全局缓存、浏览器中的 ...
    99+
    2022-11-13
    JavaScript设计模式 JavaScript单例模式
  • 使用java怎么实现一个饿汉模式单例
    今天就跟大家聊聊有关使用java怎么实现一个饿汉模式单例,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。常用的java框架有哪些1.SpringMVC,Spring Web MVC是一...
    99+
    2023-06-14
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作