文章目录

  • 1. 饿汉式
  • 2. 懒汉式
  • 3. DCL 双重校验锁懒汉式
  • 4. 通过反射破坏DCL & 加锁阻止
  • 5. 通过不调用 getInstance() 来破坏单例
  • 6. 通过反射来干扰信号量,从而破坏单例
  • 7. 通过枚举类实现单例,可以防止反射破坏单例
  • 学 JUC 的时候顺便摸了下单例模式,看的是狂神的JUC教程~

1. 饿汉式

  • 缺点:可能会造成浪费空间
public class HungrySingleton {// 缺点:可能造成浪费空间(如下四行占用不必要空间)private byte[] data1 = new byte[1024 * 1024];private byte[] data2 = new byte[1024 * 1024];private byte[] data3 = new byte[1024 * 1024];private byte[] data4 = new byte[1024 * 1024];// 唯一实例private static HungrySingleton INSTANCE = new HungrySingleton();// 私有化构造函数private HungrySingleton() {}// 获取实例的入口public static HungrySingleton getInstance() {return INSTANCE;}public static void main(String[] args) {System.out.println(HungrySingleton.getInstance());}
}

2. 懒汉式

  • 避免了空间的浪费,单线程安全,但是多线程不安全。
public class LazySingleton {// 懒起来了,用到才实例化private static LazySingleton INSTANCE = null;private LazySingleton(){}// 单线程没事,但是多线程情况不安全public static LazySingleton getInstance(){if(INSTANCE == null){INSTANCE = new LazySingleton();}return INSTANCE;}
}

3. DCL 双重校验锁懒汉式

  • Double Check Lock,判断了两次,加了一个 synchronized 锁住代码块
  • 为什么要判断两次:可能多个进程卡在 synchronized 锁这步,所以进去后还要再判断一次
  • 不安全的原因:指令重排(见代码注释)
  • 解决方法:加 volatile 关键字禁止指令重排(见第三行注释代码)
public class DCLSingleton {private static DCLSingleton INSTANCE = null;// private volatile static DCLSingleton INSTANCE = null;private DCLSingleton(){}// 双重校验锁public static DCLSingleton getInstance(){if(INSTANCE == null){synchronized (DCLSingleton.class){if(INSTANCE == null){// 但是实际上,这一行代码还是不安全的,// 因为可能会指令重排,导致 return 未初始化完成的 INSTANCE// 解决方法是给 INSTANCE 加上一个 volatile 关键字禁止指令重排(如注释)INSTANCE = new DCLSingleton();// 然而即使如此,还是可以用反射来破坏单例模式}}}return INSTANCE;}
}
  • 从字节码指令角度,分析指令重排影响:(图源黑马JVM视频,侵删)
  • 可以看到,JIT 编译器可能会优化,先 putstatic,把引用地址赋予 INSTANCE,然后再 Method 来初始化
  • 但是,并发情况下,可能在赋予引用后,init 前,有其他线程访问了 getInstance(),获得了一个还未引用的 INSTANCE,导致出错。

4. 通过反射破坏DCL & 加锁阻止

  • 可以通过反射 setAccessible 无视私有,使用构造器来破坏单例(见 main() 代码)
  • 阻止方法:在构造器再加一个 synchronized 锁,并且进行反射破坏判断并抛出异常
public class TripleSingleton {private static TripleSingleton INSTANCE = null;private TripleSingleton(){// 再加一重锁~防止反射破坏单例synchronized (TripleSingleton.class){if(INSTANCE != null){throw new RuntimeException("不要通过反射来破坏单例模式");}}}// 三重校验锁public static TripleSingleton getInstance(){if(INSTANCE == null){synchronized (TripleSingleton.class){if(INSTANCE == null){INSTANCE = new TripleSingleton();}}}return INSTANCE;}public static void main(String[] args) throws Exception{System.out.println(TripleSingleton.getInstance());// 使用反射来破坏单例模式;null 代表无参构造器Constructor<TripleSingleton> constructor = TripleSingleton.class.getDeclaredConstructor(null);// 无视私有,破坏单例模式constructor.setAccessible(true);System.out.println(constructor.newInstance());}
}

5. 通过不调用 getInstance() 来破坏单例

  • 既然 4 是基于 INSTANCE != null 的情况来判断,那么我们只要不理 INSTANCE 就可以破坏单例模式了~
  • 只通过反射得到的构造器来创造多个实例
  • 解决方法:加一个信号量 flag,在构造函数中防止这种破坏方式
public class TripleSingleton2 {private static TripleSingleton2 INSTANCE = null;// 再加一个信号量~防止破坏单例private static boolean flag = false;private TripleSingleton2(){// 再加一重锁~防止破坏单例synchronized (TripleSingleton2.class){if(flag == false){flag = true;}else {throw new RuntimeException("不要通过反射来破坏单例模式");}}}// 三重校验锁public static TripleSingleton2 getInstance(){if(INSTANCE == null){synchronized (TripleSingleton2.class){if(INSTANCE == null){INSTANCE = new TripleSingleton2();}}}return INSTANCE;}public static void main(String[] args) throws Exception{// 不调用getInstance(),直接反射多个实例的情况// 使用反射来破坏单例模式;null 代表无参构造器Constructor<TripleSingleton2> constructor = TripleSingleton2.class.getDeclaredConstructor(null);// 无视私有,破坏单例模式constructor.setAccessible(true);System.out.println(constructor.newInstance());System.out.println(constructor.newInstance());}
}

6. 通过反射来干扰信号量,从而破坏单例

  • 只要通过反射来干扰信号量,就可以继续破坏单例模式了~(见 main() 代码)
package singletons;import java.lang.reflect.Constructor;
import java.lang.reflect.Field;/*** @Author Fwy^ ^* @Date*/
public class TripleSingleton3 {private static TripleSingleton3 INSTANCE = null;// 再加一个信号量~防止破坏单例private static boolean flag = false;private TripleSingleton3(){// 再加一重锁~防止破坏单例synchronized (TripleSingleton3.class){if(flag == false){flag = true;}else {throw new RuntimeException("不要通过反射来破坏单例模式");}}}// 三重校验锁public static TripleSingleton3 getInstance(){if(INSTANCE == null){synchronized (TripleSingleton3.class){if(INSTANCE == null){INSTANCE = new TripleSingleton3();}}}return INSTANCE;}public static void main(String[] args) throws Exception{// 使用反射来干扰信号量Field flag = TripleSingleton3.class.getDeclaredField("flag");flag.setAccessible(true);// 使用反射来破坏单例模式;null 代表无参构造器Constructor<TripleSingleton3> constructor = TripleSingleton3.class.getDeclaredConstructor(null);// 无视私有,破坏单例模式constructor.setAccessible(true);TripleSingleton3 instance1 = constructor.newInstance();flag.set(instance1, false);TripleSingleton3 instance2 = constructor.newInstance();System.out.println(instance1 + "\n" + instance2);}
}

7. 通过枚举类实现单例,可以防止反射破坏单例

  • 为了进行反射破坏,先要获取 enum 类的构造器
  • 观测源码,发现有无参构造函数,然而实际上这个是假的= =。
  • 使用 javap 反编译,会发现还是有无参构造函数
  • 使用 jad,会找到一个 private EnumSingle(String s, int i) 的构造函数,成功~
public enum EnumSingleton {// 枚举实现单例,可以防止通过反射破坏单例模式~INSTANCE;public static void main(String[] args) throws Exception{System.out.println(EnumSingleton.INSTANCE);Constructor<EnumSingleton> constructor = EnumSingleton.class.getDeclaredConstructor(String.class, int.class);System.out.println(constructor.newInstance());}
}
  • 会爆出这个错误:
  • 更加深入可以去看看 Enum 的源码~

【学习笔记】单例模式(枚举、校验锁、volatile、反射破坏)相关推荐

  1. UE4 Material 101学习笔记——23-29 水涟漪/水深/折射反射/Gerstner海浪/波光焦散/泡沫/FlowMap

    UE4 Material 101学习笔记--23-29 水涟漪/水深/折射反射/Gerstner海浪/波光焦散/泡沫/FlowMap Lec23 水的表面涟漪 Water Ripples Shader ...

  2. 几何光学学习笔记(14)- 4.3 反射棱镜

    几何光学学习笔记(14)- 4.3 反射棱镜 4.3 反射棱镜 1.反射棱镜的类型 1.1反射棱镜的构成 1.2棱镜的类型 2.屋脊棱镜 3.三面直角棱镜(立方角锥棱镜) 4.棱镜的组合 分光棱镜 对 ...

  3. 博弈论学习笔记——纳什均衡与社会最优、破坏均衡的方法

    系列文章 博弈论学习笔记--博弈收益期望的计算与决策 博弈论学习笔记--纳什均衡与社会最优.破坏均衡的方法 博弈论学习笔记--拍卖原理 简介 在囚徒困境的情境中,二者的博弈会达到一个纳什均衡,即都会选 ...

  4. 单例模式双重校验锁_被面试官虐过之后,他轻蔑的问我:你还说你了解单例模式吗?...

    单例,大家肯定都不陌生,这是Java中很重要的一个设计模式.其实单例模式看上去简单,实际上却有很多容易被忽视的地方,因为他涉及到一些线程安全的问题,稍不留神就可能入坑. 本文,就通过一次面试经历来深入 ...

  5. 【设计模式】学习笔记---单例模式

    概述 学习设计模式,死记硬背是没用的,还要从实践中理解 日常应用中,设计模式从来都不是单个设计模式独立使用的.在实际应用中,通常多个设计模式混合使用,你中有我,我中有你.下图完整地描述了设计模式之间的 ...

  6. 单例模式双重校验锁_滴滴面试官:如何实现一个线程安全的单例模式

    单例模式作为最常见的设计模式,有很多实现方式,今天介绍一下单例模式相关的内容. 什么是单例模式 从字面上理解,单例模式需要确保一个类只有一个对象.比如线程池.缓存.日志对象.打印机驱动对象.显卡驱动对 ...

  7. 多线程学习笔记一之内置锁

    目录 多线程的概念 创建线程 线程状态 synchronized同步锁 volatile 多线程的概念 什么是多线程   操作系统在运行程序时,就会为其创建一个进程,我们可以把进程理解为"运 ...

  8. C#设计模式学习笔记-单例模式

    最近在学设计模式,学到创建型模式的时候,碰到单例模式(或叫单件模式),现在整理一下笔记. 在<Design Patterns:Elements of Resuable Object-Orient ...

  9. Struts 学习笔记2(输入校验、国际化、异常处理)

    项目文件结构 项目源文件下载地址:http://dl.dbank.com/c05qyg3yir Struts2的输入校验 Struts2输入校验.执行流程: 1)首先进行类型转换 2)然后进行输入校验 ...

  10. JDK源码学习笔记——Enum枚举使用及原理

    一.为什么使用枚举 什么时候应该使用枚举呢?每当需要一组固定的常量的时候,如一周的天数.一年四季等.或者是在我们编译前就知道其包含的所有值的集合. 利用 public final static 完全可 ...

最新文章

  1. Java文件能编译成lib吗_Makefile用于将一些.cpp和.h编译成lib
  2. 计算机指令格式哪几部分组成,计算机的指令格式,通常是由()两部分组成。 - 百科题库网...
  3. 字符串hash(一)
  4. C语言 删除文件 M,最全的C盘可删除文件清单
  5. ABP入门系列(15)——创建微信公众号模块
  6. 蓝桥杯2017年第八届C/C++省赛B组第四题-方格分割
  7. Mac SecureCRT 下载、安装详细步骤
  8. 《托马斯微积分》阅读笔记2
  9. ubuntu安装aircrack-ng/reaver/minidwep-gtk用来跑pin
  10. Nginx搭建反向代理服务器
  11. gxf------阿里云学生机部署Django项目2020
  12. 京东商品详情数据接口(APP端,H5端),监控京东商品历史价格及价格走势,接口代码对接教程
  13. js数组操作的一些方法在面试题的使用
  14. Entity Framework Core系列教程-25-Entity Framework Core日志
  15. [附源码]java毕业设计旅游网站
  16. 【中文】【吴恩达课后编程作业】Course 4 - 卷积神经网络 - 第二周作业
  17. 软考 - 01 考试范围及知识点
  18. 第01章 Spring-Boot 应用文件application配置
  19. 快来帮您一分钟了解移动互联网
  20. Ubuntu 18下OpenCV3.4的安装与使用示例

热门文章

  1. windows查看dll库接口函数
  2. simulink显示多个数据_Matlab/Simulink与自动驾驶(ADAS)算法开发
  3. docker常用命令_docker常用命令整理
  4. 登录python自动化_Appium+Python实现自动化登录
  5. OpenCV学习笔记(十二):边缘检测:Canny(),Sobel(),Laplace(),Scharr滤波器
  6. QT的QStackedLayout
  7. OpenCV的基本模块介绍
  8. 深度学习中的信息论——交叉熵
  9. 冈萨雷斯《数字图像处理》读书笔记(九)——形态学图像处理
  10. Struts2源码阅读(三)_DispatcherConfigurationProvider