单例模式

本文探讨了单例模式的实现方式,包括饿汉式、懒汉式,以及如何通过反射破坏。重点介绍了使用枚举创建的单例模式,解释其如何防止反射攻击,并展示了实际的反编译案例。最后总结了单例模式的安全性和保护措施的重要性。
摘要由CSDN通过智能技术生成


饿汉式 DCL懒汉式

1.饿汉式

package JUC;

/**
 * 饿汉式 浪费内存
 */
public class SingletonHungry {
	
	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 SingletonHungry() {
		System.out.println(Thread.currentThread().getName());
	}
	
	private final static SingletonHungry HUNGRY = new SingletonHungry();
	
	public static SingletonHungry getInstance() {
		return HUNGRY;
	};
	
	public static void main(String[] args) {
		for (int i = 0; i < 10; i++) {
			new Thread(()->{
				SingletonHungry.getInstance();
			}).start();
		}
	}
}

2.懒汉式

package JUC;
/**   
 * 懒汉式单例 双重检测锁+volatile防止指令重排 
 */
public class SingletonLazyMan {

	private SingletonLazyMan(){
		System.out.println(Thread.currentThread().getName());
	}
	
	private volatile static SingletonLazyMan lazyMan; 
	
	//双重检测锁 DCL懒汉式
	public static SingletonLazyMan getInstance() {
		if (lazyMan==null) {
			synchronized (SingletonLazyMan.class) {
				if (lazyMan==null) {
					lazyMan = new SingletonLazyMan();
					/**
					 * 不是原子性操作
					 * 1.分配内存空间
					 * 2.执行构造方法,初始化对象
					 * 3.把对象指向这个空间
					 * 
					 * 指令重排 123  
					 * 但是如果执行132 A
					 * 				B
					 */
				}
			}
		}
		return lazyMan; //此时SingletonLazyMan还没有完成构造
	}
	
	public static void main(String[] args) {
		for (int i = 0; i < 10; i++) {
			new Thread(()->{
				SingletonLazyMan.getInstance();
			}).start();
		}
	}
}

2.1反射破坏单例

package JUC;

import java.lang.reflect.Constructor;

/**   
 * 懒汉式单例 双重检测锁+volatile防止指令重排 
 */
public class SingletonLazyMan {

	private SingletonLazyMan(){
		System.out.println(Thread.currentThread().getName());
	}
	
	private volatile static SingletonLazyMan lazyMan; 
	
	//双重检测锁 DCL懒汉式
	public static SingletonLazyMan getInstance() {
		if (lazyMan==null) {
			synchronized (SingletonLazyMan.class) {
				if (lazyMan==null) {
					lazyMan = new SingletonLazyMan();
					/**
					 * 不是原子性操作
					 * 1.分配内存空间
					 * 2.执行构造方法,初始化对象
					 * 3.把对象指向这个空间
					 * 
					 * 指令重排 123  
					 * 但是如果执行132 A
					 * 				B
					 */
				}
			}
		}
		return lazyMan; //此时SingletonLazyMan还没有完成构造
	}
	
	public static void main(String[] args) throws Exception, SecurityException {
		SingletonLazyMan lazyMan = SingletonLazyMan.getInstance();
		Constructor<SingletonLazyMan> constructor = SingletonLazyMan.class.getDeclaredConstructor();
		constructor.setAccessible(true);
		SingletonLazyMan lazyMan2 = constructor.newInstance();
		
		if (lazyMan == lazyMan2) {
			System.out.println("相等");
		}else {
			System.out.println("不相等");
		}
	}
}

在这里插入图片描述

2.2改良懒汉式单例 (三重检测)

package JUC;

import java.lang.reflect.Constructor;

/**   
 * 懒汉式单例 三重检测锁+volatile防止指令重排 
 */
public class SingletonLazyMan {

	private SingletonLazyMan(){
		//三重检测
		synchronized (SingletonLazyMan.class) {
			if (lazyMan!=null) {
				throw new RuntimeException("不要试图使用反射破坏异常");
			}
		}
		System.out.println(Thread.currentThread().getName());
	}
	
	private volatile static SingletonLazyMan lazyMan; 
	
	//双重检测锁 DCL懒汉式
	public static SingletonLazyMan getInstance() {
		if (lazyMan==null) {
			synchronized (SingletonLazyMan.class) {
				if (lazyMan==null) {
					lazyMan = new SingletonLazyMan();
					/**
					 * 不是原子性操作
					 * 1.分配内存空间
					 * 2.执行构造方法,初始化对象
					 * 3.把对象指向这个空间
					 * 
					 * 指令重排 123  
					 * 但是如果执行132 A
					 * 				B
					 */
				}
			}
		}
		return lazyMan; //此时SingletonLazyMan还没有完成构造
	}
	
	public static void main(String[] args) throws Exception, SecurityException {
		//SingletonLazyMan lazyMan = SingletonLazyMan.getInstance();
		Constructor<SingletonLazyMan> constructor = SingletonLazyMan.class.getDeclaredConstructor();
		constructor.setAccessible(true);
		SingletonLazyMan lazyMan2 = constructor.newInstance();
		SingletonLazyMan lazyMan3 = constructor.newInstance();
		
		if (lazyMan == lazyMan2) {
			System.out.println("相等");
		}else {
			System.out.println("不相等");
		}
	}
}

在这里插入图片描述

2.3改良单例信号灯法

package JUC;

import java.lang.reflect.Constructor;

/**   
 * 懒汉式单例 三重检测锁+volatile防止指令重排 
 */
public class SingletonLazyMan {

	//信号灯
	private static boolean gouzi = false;
	
	private SingletonLazyMan(){
		//三重检测
		synchronized (SingletonLazyMan.class) {
			if (gouzi==false) {
				gouzi = true;
			} else {
				throw new RuntimeException("不要试图使用反射破坏异常");
			}
		}
		System.out.println(Thread.currentThread().getName());
	}
	
	private volatile static SingletonLazyMan lazyMan; 
	
	//双重检测锁 DCL懒汉式
	public static SingletonLazyMan getInstance() {
		if (lazyMan==null) {
			synchronized (SingletonLazyMan.class) {
				if (lazyMan==null) {
					lazyMan = new SingletonLazyMan();
					/**
					 * 不是原子性操作
					 * 1.分配内存空间
					 * 2.执行构造方法,初始化对象
					 * 3.把对象指向这个空间
					 * 
					 * 指令重排 123  
					 * 但是如果执行132 A
					 * 				B
					 */
				}
			}
		}
		return lazyMan; //此时SingletonLazyMan还没有完成构造
	}
	
	public static void main(String[] args) throws Exception, SecurityException {
		//SingletonLazyMan lazyMan = SingletonLazyMan.getInstance();
		Constructor<SingletonLazyMan> constructor = SingletonLazyMan.class.getDeclaredConstructor();
		constructor.setAccessible(true);
		SingletonLazyMan lazyMan2 = constructor.newInstance();
		SingletonLazyMan lazyMan3 = constructor.newInstance();
		
		if (lazyMan == lazyMan2) {
			System.out.println("相等");
		}else {
			System.out.println("不相等");
		}
	}
}

在这里插入图片描述

2.4破坏单例信号灯

package JUC;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

/**   
 * 懒汉式单例 三重检测锁+volatile防止指令重排 
 */
public class SingletonLazyMan {

	//信号灯
	private static boolean gouzi = false;
	
	private SingletonLazyMan(){
		//三重检测
		synchronized (SingletonLazyMan.class) {
			if (gouzi==false) {
				gouzi = true;
			} else {
				throw new RuntimeException("不要试图使用反射破坏异常");
			}
		}
		System.out.println(Thread.currentThread().getName());
	}
	
	private volatile static SingletonLazyMan lazyMan; 
	
	//双重检测锁 DCL懒汉式
	public static SingletonLazyMan getInstance() {
		if (lazyMan==null) {
			synchronized (SingletonLazyMan.class) {
				if (lazyMan==null) {
					lazyMan = new SingletonLazyMan();
					/**
					 * 不是原子性操作
					 * 1.分配内存空间
					 * 2.执行构造方法,初始化对象
					 * 3.把对象指向这个空间
					 * 
					 * 指令重排 123  
					 * 但是如果执行132 A
					 * 				B
					 */
				}
			}
		}
		return lazyMan; //此时SingletonLazyMan还没有完成构造
	}
	
	public static void main(String[] args) throws Exception, SecurityException {
		//获取属性 改为private允许访问
		Field[] fields = SingletonLazyMan.class.getDeclaredFields();
		for (Field field : fields) {
			System.out.println(field.getName());
			field.setAccessible(true);
		}
		Field field = SingletonLazyMan.class.getDeclaredField("gouzi");
		
		//SingletonLazyMan lazyMan = SingletonLazyMan.getInstance();
		Constructor<SingletonLazyMan> constructor = SingletonLazyMan.class.getDeclaredConstructor();
		constructor.setAccessible(true);
		SingletonLazyMan lazyMan2 = constructor.newInstance();

		//修改属性
		field.set(lazyMan2, false);
		
		SingletonLazyMan lazyMan3 = constructor.newInstance();
		
		if (lazyMan3 == lazyMan2) {
			System.out.println("相等");
		}else {
			System.out.println("不相等");
		}
	}
}

在这里插入图片描述

3.使用枚举 保护单例模式(防止反射攻击)

3.1为什么枚举可以保护 单例模式?

在这里插入图片描述

在这里插入图片描述

3.2 枚举创建单例

package JUC;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**   
 * 枚举懒汉式单例
 */
public enum EnumSingletonLazyMan {

	INSTANCE;
	
	public EnumSingletonLazyMan getInstance() {
		return INSTANCE;
	}
}

class TTTT{
	
	public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		EnumSingletonLazyMan e1 = EnumSingletonLazyMan.INSTANCE;
		
		Constructor<EnumSingletonLazyMan> constructor = EnumSingletonLazyMan.class.getDeclaredConstructor();
		constructor.setAccessible(true);
		
		//in thread "main" java.lang.NoSuchMethodException: JUC.EnumSingletonLazyMan.<init>()
		EnumSingletonLazyMan e2 = constructor.newInstance();
		
		if (e1 == e2) {
			System.out.println("相等");
		}else {
			System.out.println("不相等");
		}
	}
}

而得到的是这个
在这里插入图片描述
并非
在这里插入图片描述

3.3查看底层 反编译

找到.class目录下
在这里插入图片描述
进行反编译
在这里插入图片描述
也是有空参的,

3.4 使用jad.exe

在这里插入图片描述
在这里插入图片描述
看到最终源码 这里是有参构造器。

再次修改代码
在这里插入图片描述

4.总结

单例不安全,无论怎么修改都能被反射破解。
枚举创建单例安全,可以通过 专业编译器反编译出来,(枚举类)真实的源码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只小小狗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值