目录
单例模式
核心作用:
保证一个类只有一个实例,并且提供一个访问该实例的全局访问点
常见场景:
- Windows的任务管理器
- Windows的回收站
- 项目中,读取配置文件的类,一般也只有一个对象,没必要每次都去new对象读取
- 网站的计数器一般也会采用单例模式,可以保证同步
- 数据库连接池的设计一般也是单例模式
- 在Servlet编程中,每个Servlet也是单例的
- 在Spring中,每个Bean默认就是单例的
- ……
饿汉式
/**
* @author gh Email:@2495140780qq.com
* @Description
* @date 2022-03-08-下午 10:55
*
*
* 单例模式思想 :
* 构造器私有 保证内存中 只有 一个对象
*/
// 饿汉式 单例
public class _01Hungry {
/**
* 饿汉式 :
* 浪费内存
* 一上来就把内存 内存中的 所有 数据 全部加载进来 非常的耗内存
*/
//可能会浪费空间
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 _01Hungry () {
}
private final static _01Hungry HUNGRY = new _01Hungry ();
public static _01Hungry getInstance() {
return HUNGRY;
}
}
懒汉式
1. 懒汉式 单线程 ok
//懒汉式
public class LazyMan1 {
private LazyMan1() {
}
private static LazyMan1 lazyMan1;
public static LazyMan1 getInstance() {
if (lazyMan1 == null) {
lazyMan1 = new LazyMan1();
}
return lazyMan1;
}
//单线程下 ok
// 多线程 并发
}
2.懒汉式 多线程 no
//懒汉式
public class LazyMan2 {
private LazyMan2() {
System.out.println(Thread.currentThread().getName() + "ok" );
}
private static LazyMan2 lazyMan2;
public static LazyMan2 getInstance() {
if (lazyMan2 == null) {
lazyMan2 = new LazyMan2();
}
return lazyMan2;
}
//多线线程 并发
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
LazyMan2.getInstance();
}).start();
}
}
}
//发现问题 需要加锁 加锁之前 还容易被两个线程 拿到 做两次检测
3.懒汉式 完整的双重检测锁DCL 加 原子性操作
加volatile,防止指令重排
//懒汉式 完整的双重检测锁 加 原子性操作
public class LazyMan3 {
private LazyMan3() {
System.out.println(Thread.currentThread().getName() + "ok");
}
private volatile static LazyMan3 lazyMan3;
// 双重检测所模式 的 懒汉式 DCL 懒汉式
public static LazyMan3 getInstance() {
if (lazyMan3 == null) {
synchronized (LazyMan3.class) {
if (lazyMan3 == null) {
lazyMan3 = new LazyMan3(); //不是原子性操作
/**
* 1. 分配内存空间
* 2, 执行构造方法,初始化对象
* 3. 把这个对象指向这个空间
*
* 指令重排
* 123
* 132 A
* B 此时LazyMan3 还没有完成构造
* LazyMan3 避免指令重排 加 volatile
*/
}
}
}
return lazyMan3;
}
//多线线程 并发
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
LazyMan3.getInstance();
}).start();
}
}
}
4. 反射破坏单例
//懒汉式 反射破坏单例
public class LazyMan4 {
private LazyMan4() {
System.out.println(Thread.currentThread().getName() + "ok");
}
private volatile static LazyMan4 lazyMan4;
// 双重检测所模式 的 懒汉式 DCL 懒汉式
public static LazyMan4 getInstance() {
if (lazyMan4 == null) {
synchronized (LazyMan4.class) {
if (lazyMan4 == null) {
lazyMan4 = new LazyMan4(); //不是原子性操作
/**
* 1. 分配内存空间
* 2, 执行构造方法,初始化对象
* 3. 把这个对象指向这个空间
*
* 指令重排
* 123
* 132 A
* B 此时LazyMan3 还没有完成构造
* LazyMan3 避免指令重排 加 volatile
*/
}
}
}
return lazyMan4;
}
//反射 反射可以破坏单例
public static void main(String[] args) throws Exception {
LazyMan4 instance1 = LazyMan4.getInstance(); //获得第一个对象
Constructor<LazyMan4> declaredConstructor = LazyMan4.class.getDeclaredConstructor(null); //空参构造器
declaredConstructor.setAccessible(true);//无视私有构造器
LazyMan4 instance2 = declaredConstructor.newInstance();
System.out.println(instance1.equals(instance2)); //false
System.out.println(instance1); //false
System.out.println(instance2); //false
}
}
5.解决反射破坏单例 双重检测 升级 三级检测
//懒汉式 解决反射破坏单例 双重检测 升级 三级检测
public class LazyMan5 {
private LazyMan5() {
synchronized(LazyMan5.class) {
if(lazyMan5 != null) {
throw new RuntimeException("不要试图使用反射破坏异常");
}
}
}
private volatile static LazyMan5 lazyMan5;
// 双重检测所模式 的 懒汉式 DCL 懒汉式
public static LazyMan5 getInstance() {
if (lazyMan5 == null) {
synchronized (LazyMan5.class) {
if (lazyMan5 == null) {
lazyMan5 = new LazyMan5(); //不是原子性操作
}
}
}
return lazyMan5;
}
//反射 反射可以破坏单例
public static void main(String[] args) throws Exception {
LazyMan5 instance1 = LazyMan5.getInstance();
Constructor<LazyMan5> declaredConstructor = LazyMan5.class.getDeclaredConstructor();
declaredConstructor.setAccessible(true);//无视私有构造器
LazyMan5 instance2 = declaredConstructor.newInstance();
System.out.println(instance1.equals(instance2)); //异常
System.out.println(instance1);
System.out.println(instance2);
}
}
6.用反射创建对象 破坏三重检测
但是仍然可以通过如下方式破坏:
//反射 反射可以破坏单例
public static void main(String[] args) throws Exception {
// LazyMan6 instance1 = LazyMan6.getInstance(); 常见创建这个对象
Constructor<LazyMan6> declaredConstructor = LazyMan6.class.getDeclaredConstructor();
declaredConstructor.setAccessible(true);//无视私有构造器
LazyMan6 instance1 = declaredConstructor.newInstance(); // 反射 new newInstance
LazyMan6 instance2 = declaredConstructor.newInstance();
System.out.println(instance1.equals(instance2)); //false
System.out.println(instance1);
System.out.println(instance2);
}
}
7. 解决用反射创建 对象 秘钥
设置一个别人不知道的变量
//懒汉式 解决用反射创建 对象 秘钥
public class LazyMan7 {
private static boolean zhangsan = false; //秘钥
private LazyMan7() {
synchronized(LazyMan7.class) {
if (zhangsan == false) {
zhangsan = true;
}else {
throw new RuntimeException("不要试图使用反射破坏异常");
}
}
}
private volatile static LazyMan7 lazyMan7;
// 双重检测所模式 的 懒汉式 DCL 懒汉式
public static LazyMan7 getInstance() {
if (lazyMan7 == null) {
synchronized (LazyMan7.class) {
if (lazyMan7 == null) {
lazyMan7 = new LazyMan7(); //不是原子性操作
}
}
}
return lazyMan7;
}
//反射 反射可以破坏单例
public static void main(String[] args) throws Exception {
// LazyMan6 instance1 = LazyMan6.getInstance(); 常见创建这个对象
Constructor<LazyMan7> declaredConstructor = LazyMan7.class.getDeclaredConstructor();
declaredConstructor.setAccessible(true);//无视私有构造器
LazyMan7 instance1 = declaredConstructor.newInstance(); // 反射 new newInstance
LazyMan7 instance2 = declaredConstructor.newInstance();
System.out.println(instance1.equals(instance2)); //异常
System.out.println(instance1);
System.out.println(instance2);
}
}
8.破坏 两个反射创建 对象 秘钥
//懒汉式 破坏 两个反射创建 对象 秘钥
public class LazyMan8 {
private static boolean zhangsan = false; //秘钥
private LazyMan8() {
synchronized(LazyMan8.class) {
if (zhangsan == false) {
zhangsan = true;
}else {
throw new RuntimeException("不要试图使用反射破坏异常");
}
}
}
private volatile static LazyMan8 lazyMan8;
// 双重检测所模式 的 懒汉式 DCL 懒汉式
public static LazyMan8 getInstance() {
if (lazyMan8 == null) {
synchronized (LazyMan8.class) {
if (lazyMan8 == null) {
lazyMan8 = new LazyMan8(); //不是原子性操作
}
}
}
return lazyMan8;
}
//反射 反射可以破坏单例
public static void main(String[] args) throws Exception {
// LazyMan6 instance1 = LazyMan6.getInstance(); 常见创建这个对象
Field zhangsan = LazyMan8.class.getDeclaredField("zhangsan"); //获取字段
zhangsan.setAccessible(true);
Constructor<LazyMan8> declaredConstructor = LazyMan8.class.getDeclaredConstructor();
declaredConstructor.setAccessible(true);//无视私有构造器
LazyMan8 instance1 = declaredConstructor.newInstance(); // 反射 new newInstance
zhangsan.set(instance1, false);
LazyMan8 instance2 = declaredConstructor.newInstance();
System.out.println(instance1.equals(instance2)); //false
System.out.println(instance1);
System.out.println(instance2);
}
}
单例不安全,使用枚举
枚举自带单例模式
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance() {
return INSTANCE;
}
}
test
class Test{
public static void main(String[] args) {
EnumSingle instance1 = EnumSingle.INSTANCE;
EnumSingle instance2 = EnumSingle.INSTANCE;
System.out.println(instance1.equals(instance2)); //true
System.out.println(instance1); //INSTANCE
System.out.println(instance2); //INSTANCE
}
}
破坏枚举,发现问题
class Test{
public static void main(String[] args) throws Exception {
EnumSingle instance1 = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
EnumSingle instance2 = declaredConstructor.newInstance();
System.out.println(instance1.equals(instance2)); //true
System.out.println(instance1); //INSTANCE
System.out.println(instance2); //INSTANCE
}
}
正常应该报错
Cannot reflectively create enum objects 不能反射地创建enum对象
idea欺骗了我们
class Test{
public static void main(String[] args) throws Exception {
EnumSingle instance1 = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
EnumSingle instance2 = declaredConstructor.newInstance();
//NoSuchMethodException: com.gh.EnumSingle.<init>() 异常 类里面没有空参的构造器
System.out.println(instance1.equals(instance2)); //true
System.out.println(instance1); //INSTANCE
System.out.println(instance2); //INSTANCE
}
}
使用jad工具反编译为java
枚举类型的最终反编译源码:
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name: EnumSingle.java
public final class EnumSingle extends Enum
{
public static EnumSingle[] values()
{
return (EnumSingle[])$VALUES.clone();
}
public static EnumSingle valueOf(String name)
{
return (EnumSingle)Enum.valueOf(com/kuang/single/EnumSingle, name);
}
private EnumSingle(String s, int i)
{
super(s, i);
}
public EnumSingle getInstance()
{
return INSTANCE;
}
public static final EnumSingle INSTANCE;
private static final EnumSingle $VALUES[];
static
{
INSTANCE = new EnumSingle("INSTANCE", 0);
$VALUES = (new EnumSingle[] {
INSTANCE
});
}
}