【设计模式】单例模式
慢慢来,才比较快
单例模式属于对象创建型模式,它要求系统中只生成一个实例,提供了一种创建对象的最佳方式
?那么如何保证类中只有一个实例并且这个实例易于被访问呢
一个很好的解决的方法就是让这个类本身保存它的唯一实例,这个类需要保证没有其他实例被创建,并且它可以提供访问该实例的方法。
所以我们可以得出单例模式的定义:单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类,它提供一个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");
- 表示如果是枚举则不能反射。
单例模式总结:
优点:
- 在内存中只有一个实例,减少了资源的开销
- 可以避免对资源的多重占用
缺点:
- 没有接口,扩展困难
单例模式可以跟工厂模式搭配使用。
【设计模式】单例模式相关推荐
- Python设计模式-单例模式
Python设计模式-单例模式 基于Python3.5.2,代码如下 #coding:utf-8 import threading import timeclass Singleton(object) ...
- Android设计模式——单例模式(Singleton)
二十三种设计模式分为三大类: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接模式.组合模式.享元 ...
- 【学习笔记】ABAP OOD设计模式 - 单例模式
ABAP OOD设计模式 - 单例模式 整理转自-<SAP ABAP 面向对象程序设计(原则.模式及实践)> 单例模式(Singleton Pattern)是常用的且较为简单的软件设计模式 ...
- Go 语言实现 23 种设计模式 单例模式
Go 语言实现 23 种设计模式 单例模式 单例模式 单例模式是一种常用的软件设计模式,在使用过程中,单例对象的类只有一个实例.使用单例模式,1 可以节省内存等资源,例如windows操作系统的资源管 ...
- java singleton inner class_Java面向对象设计模式-单例模式
Java面向对象设计模式-单例模式 2020-05-28 589 0 单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问点,有多重实现方式. 一.饿汉式单例模式,构造方法私有化,在加载类Sin ...
- Java开发中常用的设计模式-单例模式
单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式. Java开发中常用的设计模式-单例模式 单例模式有3个特点: 单例类只有一个实例对象: 该单例对象必须 ...
- 炒冷饭系列:设计模式 单例模式
2019独角兽企业重金招聘Python工程师标准>>> 炒冷饭系列:设计模式 单例模式 摘要: 原创出处: http://www.cnblogs.com/Alandre/ 泥沙砖瓦浆 ...
- 设计模式-单例模式-注册式单例模式-枚举式单例模式和容器式单例模式在Java中的使用示例
场景 设计模式-单例模式-饿汉式单例模式.懒汉式单例模式.静态内部类在Java中的使用示例: 设计模式-单例模式-饿汉式单例模式.懒汉式单例模式.静态内部类在Java中的使用示例_霸道流氓气质的博客- ...
- 设计模式----创建型设计模式(单例模式、工厂方法模式、构建者模式)
创建型设计模式 单例模式(Singleton Pattern) 单例模式介绍 代码演示 饿汉式(静态常量) 饿汉式(静态代码块) 懒汉式(线程不安全) 懒汉式(线程安全,同步方法) 懒汉式(线程安全, ...
- 小视频源码,设计模式单例模式
小视频源码,设计模式单例模式实现的相关代码 .一,单线程时候推荐 /*** Created by Shinelon on 2018/10/11.* 单利模式 懒汉式 -->单线程推荐使用*/pu ...
最新文章
- C#百度OCR-本地和网络图片识别文字
- 如何组建开发团队-建立畅通的沟通渠道
- VTK:图表之ColorVertexLabels
- 【剑指offer】——求出一个正整数的质数因子(Python)
- C++ 学习之旅(7)——指针pointer
- 本地修改PHP修改文件,PHP脚本批量修改本地文件名
- 【Ubuntu18.04安装搜狗中文输入法】
- 【DNN】——多尺度注意力
- C++核心准则​SL.con.1:标准库array或vector好于C数组
- 祛除体内湿气的最佳方法 713.html,祛除体内湿气七种有效泡水方法
- 在Word2019中,如何让回车符消失
- 猿辅导python编程老师面试_猿辅导辅导老师面试一系列的感受
- 基于神经网络 lstm的股票开盘价收盘价预测详细
- 使用Excel和OutLook实现自动发送邮件
- 把踢毽子的寒冷过程和心理描写出来
- 【解决方案】HIKSDK/大华SDK/Ehome协议视频智能分析平台EasyCVR在文物古建筑智慧防火场景的应用
- linux v4l2 示例程序,Linux关于Camera使用V4L2
- 基于CNN的自动化测试实践
- flash遍历子元件_关于flash图形元件的小知识
- 10/15/2022