慢慢来,才比较快

单例模式属于对象创建型模式,它要求系统中只生成一个实例,提供了一种创建对象的最佳方式

?那么如何保证类中只有一个实例并且这个实例易于被访问呢

一个很好的解决的方法就是让这个类本身保存它的唯一实例,这个类需要保证没有其他实例被创建,并且它可以提供访问该实例的方法。

所以我们可以得出单例模式的定义:单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类,它提供一个public方法。

因此单例模式的一个特点就是构造方法是私有的,这样就可以防止外界随意调用来产生实例(但是反射可以改变权限)

单例模式分为懒汉式和饿汉式,饿汉式是类加载的时候就初始化,而懒汉式是使用的时候才加载

单例模式的实现

饿汉式:

  • 采用静态成员变量,在类加载的时候创建实例,如果用不到这个类会造成内存资源的浪费,因为单例实例引用不可变,所以是线程安全的
public class Singleton {//1.私有构造方法private Singleton() {}//2.在本类中创建该类对象private static Singleton instance = new Singleton();public static Singleton getInstance(){return instance;}
}
  • 第一种方法的变种
public class Singleton {//私有构造方法private Singleton() {}//声明Singleton类型的变量private static Singleton instance;static {instance = new Singleton();}public static Singleton getInstance(){return instance;}
}
  • 通过枚举类实现单例模式,我们知道枚举反编译之后实际是由jvm创建一个final类去继承Enum类,通过static来加载,因此也属于饿汉式,枚举是线程安全的并且只会被装载一次,是唯一一种不会被破坏的单例模式。Java反编译以及语法糖。
public enum EnumInstance {INSTANCE;}

懒汉式:

public class Singleton {private Singleton() {}private static Singleton instance;public static Singleton getInstance(){if(instance == null){instance = new Singleton();}return instance;}
}
  • 但是这种写法,很明显不是线程安全的。如果多个线程在该类初始化之前,有大于一个线程调用了getinstance方法且lazySingleton == null 判断条件都是正确的时候,这个时候就会导致new出多个LazySingleton实例。
  • 解决的话可以在getInstance方法上加sychronized锁,但这种方式锁粒度太大,学过并发编程的同学应该都会记得有一个DCL(DoubleCheckedLocking),所以这里改进可以使用双重检查的方式
public class Singleton {private Singleton(){}private static Singleton instance;public static Singleton getInstance(){//先判断是否已经创建了实例if(instance == null){//当有多个线程进入这里时,加锁控制,只有一个线程可以进入下一步,如果条件成立则new出来//一个实例,轮到其他的线程判断的时候自然就就为假了,问题大致解决。synchronized (Singleton.class){if (instance == null){instance = new Singleton();}}}return instance;}
}

对JMM熟悉的同学应该会发现,就算用了双重检查,也还是会出现问题。因为在编译过程中,为了提高性能,编译器和处理器常常会对指令做重排序,重排序对单线程并没有影响,但是在多线程情况下就有可能得出意料之外的结果。

eg:

instance = new Singleton();

执行的时候正常是这样的:

  • 分配内存给这个对象
  • 初始化对象
  • 设置Singleton指向刚分配的内存地址。

但重排序之后就有可能变为这样:

  • 分配内存给这个对象
  • 设置Singleton指向刚分配的内存地址
  • 初始化对象

这样就还是会造成多线程的问题,比如说有两个线程 t1 和 t2 ,t1进入了第一个if之中,并且拿到了锁,进行了new Singleton();语句,在加载构造类的实例的时候,设置Singleton指向刚分配的内存地址,但是还没有初始化对象。线程2判断 if(instance == null) 为假,直接返回了 instance ,但此时还没有初始化,这就出了问题。

当然这个很好改进,从禁用重排序方面下手,添加一个volatile。

    private volatile static Singleton instance = null;

还有一个方法可以很好的避免这个问题:
定义一个静态内部类(基于类初始化的延迟加载),其静态字段实例化了一个单例。获取单例需要调用getInstance方法间接获取。

public class Singleton {private Singleton() {}private static class SingletonHolder{private static final Singleton INSTANCE = new Singleton();}public static Singleton getInstance(){return SingletonHolder.INSTANCE;}
}

单例模式的破坏和防范

序列化破坏

对饿汉式和懒汉式都起效

public class Singleton implements Serializable {//1.私有构造方法private Singleton() {}//2.在本类中创建该类对象private static Singleton instance = new Singleton();public static Singleton getInstance(){return instance;}
}
  • 单例类先实现序列化接口
/*** @Author: ekin* @Date: 2021/7/3 17:45*/public class Client {public static void main(String[] args) throws IOException, ClassNotFoundException {Singleton instance = Singleton.getInstance();ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton"));oos.writeObject(instance);File file = new File("singleton");ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));Singleton newInstance = (Singleton) ois.readObject();System.out.println(instance);System.out.println(newInstance);System.out.println(instance == newInstance);}
}
  • 测试

结果发现对象不一样,原因就涉及到序列化的底层原因了,我们先看解决方式:

单例类代码中添加下面这段代码

    private Object readResolve() {return instance;}

结果:
原因:

  • 在反序列化时,程序会检查如果单例类有readResolve方法时,则调用该方法的返回值,如果没有,则通过反射创建一个新实例

反射破坏

public class Client {public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {Class clazz = Singleton.class;Constructor constructor = clazz.getDeclaredConstructor();constructor.setAccessible(true);//打开构造器权限Singleton instance = Singleton.getInstance();Singleton newInstance = (Singleton) constructor.newInstance();System.out.println(instance);System.out.println(newInstance);System.out.println(instance == newInstance);}
}

这里强行破开了private的构造方法的权限,使得能new出来一个单例实例。

解决:

饿汉式:

    private Singleton() {if (instance != null){throw new RuntimeException("禁止反射调用");}}

懒汉式:

//当通过构造器产生实例时,该类没有实例
Singleton instance = constructor.newInstance();
Singleton newInstance = Singleton.getInstance();
  • 暂时无法防止

枚举单例模式的好处

防范序列化破坏单例:

  • 当进行反序列化读取时,调用顺序为
  • 最后一个方法读取的仍然是原来的实例

防范反射破坏单例:

  • 当进行反射的实例获取时,会出现异常
/*** @Author: ekin* @Date: 2021/7/3 20:52*/
public class Client {public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {Class clazz = com.ekin.softwaredesign.singleton.demo6.Singleton.class;Constructor constructor = clazz.getDeclaredConstructor();constructor.setAccessible(true);com.ekin.softwaredesign.singleton.demo1.Singleton instance = com.ekin.softwaredesign.singleton.demo1.Singleton.getInstance();com.ekin.softwaredesign.singleton.demo1.Singleton newInstance = (Singleton) constructor.newInstance();System.out.println(instance);System.out.println(newInstance);System.out.println(instance == newInstance);}
}
  • 原因是在 Constructor 中有这么一段话
if ((clazz.getModifiers() & Modifier.ENUM) != 0)throw new IllegalArgumentException("Cannot reflectively create enum objects");
  • 表示如果是枚举则不能反射。

单例模式总结

优点:

  • 在内存中只有一个实例,减少了资源的开销
  • 可以避免对资源的多重占用

缺点:

  • 没有接口,扩展困难

单例模式可以跟工厂模式搭配使用。

【设计模式】单例模式相关推荐

  1. Python设计模式-单例模式

    Python设计模式-单例模式 基于Python3.5.2,代码如下 #coding:utf-8 import threading import timeclass Singleton(object) ...

  2. Android设计模式——单例模式(Singleton)

    二十三种设计模式分为三大类: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接模式.组合模式.享元 ...

  3. 【学习笔记】ABAP OOD设计模式 - 单例模式

    ABAP OOD设计模式 - 单例模式 整理转自-<SAP ABAP 面向对象程序设计(原则.模式及实践)> 单例模式(Singleton Pattern)是常用的且较为简单的软件设计模式 ...

  4. Go 语言实现 23 种设计模式 单例模式

    Go 语言实现 23 种设计模式 单例模式 单例模式 单例模式是一种常用的软件设计模式,在使用过程中,单例对象的类只有一个实例.使用单例模式,1 可以节省内存等资源,例如windows操作系统的资源管 ...

  5. java singleton inner class_Java面向对象设计模式-单例模式

    Java面向对象设计模式-单例模式 2020-05-28 589 0 单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问点,有多重实现方式. 一.饿汉式单例模式,构造方法私有化,在加载类Sin ...

  6. Java开发中常用的设计模式-单例模式

    单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式. Java开发中常用的设计模式-单例模式 单例模式有3个特点: 单例类只有一个实例对象: 该单例对象必须 ...

  7. 炒冷饭系列:设计模式 单例模式

    2019独角兽企业重金招聘Python工程师标准>>> 炒冷饭系列:设计模式 单例模式 摘要: 原创出处: http://www.cnblogs.com/Alandre/ 泥沙砖瓦浆 ...

  8. 设计模式-单例模式-注册式单例模式-枚举式单例模式和容器式单例模式在Java中的使用示例

    场景 设计模式-单例模式-饿汉式单例模式.懒汉式单例模式.静态内部类在Java中的使用示例: 设计模式-单例模式-饿汉式单例模式.懒汉式单例模式.静态内部类在Java中的使用示例_霸道流氓气质的博客- ...

  9. 设计模式----创建型设计模式(单例模式、工厂方法模式、构建者模式)

    创建型设计模式 单例模式(Singleton Pattern) 单例模式介绍 代码演示 饿汉式(静态常量) 饿汉式(静态代码块) 懒汉式(线程不安全) 懒汉式(线程安全,同步方法) 懒汉式(线程安全, ...

  10. 小视频源码,设计模式单例模式

    小视频源码,设计模式单例模式实现的相关代码 .一,单线程时候推荐 /*** Created by Shinelon on 2018/10/11.* 单利模式 懒汉式 -->单线程推荐使用*/pu ...

最新文章

  1. C#百度OCR-本地和网络图片识别文字
  2. 如何组建开发团队-建立畅通的沟通渠道
  3. VTK:图表之ColorVertexLabels
  4. 【剑指offer】——求出一个正整数的质数因子(Python)
  5. C++ 学习之旅(7)——指针pointer
  6. 本地修改PHP修改文件,PHP脚本批量修改本地文件名
  7. 【Ubuntu18.04安装搜狗中文输入法】
  8. 【DNN】——多尺度注意力
  9. C++核心准则​SL.con.1:标准库array或vector好于C数组
  10. 祛除体内湿气的最佳方法 713.html,祛除体内湿气七种有效泡水方法
  11. 在Word2019中,如何让回车符消失
  12. 猿辅导python编程老师面试_猿辅导辅导老师面试一系列的感受
  13. 基于神经网络 lstm的股票开盘价收盘价预测详细
  14. 使用Excel和OutLook实现自动发送邮件
  15. 把踢毽子的寒冷过程和心理描写出来
  16. 【解决方案】HIKSDK/大华SDK/Ehome协议视频智能分析平台EasyCVR在文物古建筑智慧防火场景的应用
  17. linux v4l2 示例程序,Linux关于Camera使用V4L2
  18. 基于CNN的自动化测试实践
  19. flash遍历子元件_关于flash图形元件的小知识
  20. 10/15/2022

热门文章

  1. Excel自动为行编号
  2. 使用forEach添加序号
  3. html文件用什么打开编辑,HTML文件可用什么执行,可用什么打开并编辑?
  4. 微商城saas系统之新版架构设计
  5. vmware证书劫持网页解决方案
  6. 当媒体厂商抛弃了手机二维码厂商,谁会将在手机二维码市场的大海中破冰启航?
  7. Sigmoid函数及绘制
  8. 【springboot进阶】springboot集成fastjson(三)配置redis使用fastJson进行序列化
  9. linux下各种工具以及shell经验总结
  10. 关于Markdown编辑器的使用