单例模式(饿汉式单例、懒汉式单例、DCL模式、静态内部类实现、枚举实现)看这一篇就够了!

1.单例模式要素

	确保一个类中只有一个实例,并提供一个全局访问点
	a.私有构造方法 
    b.私有静态引用指向自己实例 
    c.以自己实例为返回值的公有静态方法 

2.饿汉式单例

public class Hungry {
	//私有的构造函数,保证外类不能实例化本类
	private Hungry (){}
	//自己创建一个类的实例化
	private final static Hungry HUNGRY= new Hungry ();
	//创建一个get方法,返回一个实例HUNGRY
	public static Hungry getInstance(){
		return HUNGRY;
	}
}

2.1饿汉式单例特点

  1. 饿汉式是典型的空间换时间,当类装载的时候就会创建类实例,不管你用不用,先创建出来,然后每次调用的时候,就不需要再判断了,节省了运行时间。

  2. 从线程安全性上讲,饿汉式是线程安全的,因为虚拟机保证只会装载一次,在装载类的时候是不会发生并发的。

3.懒汉式单例

public class LazyMan{
	private LazyMan(){
	}
	private static LazyMan lazyMan;
	public static LazyMan getInstance(){
		if(lazyMan==null){
			lazyMan=new LazyMan();
		}
		return lazyMan;
	}
}

不难发现在多线程并发时懒汉式单例并不安全,于是出现

4.懒汉式单例双重检测锁(DCL模式)

	public class LazyMan{
	private LazyMan(){
	}
	private static LazyMan lazyMan;
	//双重检测锁
	public static LazyMan getInstance(){
		if(lazyMan==null){
			synchronized (LazyMan.class){
					if(lazyMan==null){
						lazyMan=new LazyMan();
					}
			}
		}
		return lazyMan;
	}
}

在lazyMan为空时在LazyMan这个类上增加锁,保证这个类只有一个,但有可能在并发极端情况下是会出现问题的

//双重检测锁
	public static LazyMan getInstance(){
		if(lazyMan==null){
			synchronized (LazyMan.class){
					if(lazyMan==null){
						lazyMan=new LazyMan(); //不是原子性的操作
						/**
								1.分配内存空间
							    2.执行构造方法,初始化对象
							    3.把这个对象指向这个空间

							我们期望jvm上是执行123的顺序,
							但在这里会出现指令重排的现象,
							jvm执行顺序为132
							假设有a线程,执行顺序为1,2,3,
							当执行到2时,b线程开始执行顺序为
							1,3,2这时在判断lazyMan时结果不为空
							于是执行返回操作但这是的lazyMan对象
							是有问题的
						*/
					}
			}
		}
		return lazyMan;
	}

为了避免产生指令重排,增加volatile

public class LazyMan{
	private LazyMan(){
	}
	private volatile static LazyMan lazyMan;
	//双重检测锁
	public static LazyMan getInstance(){
		if(lazyMan==null){
			synchronized (LazyMan.class){
					if(lazyMan==null){
						lazyMan=new LazyMan();
					}
			}
		}
		return lazyMan;
	}
}

5.懒汉式单例(静态内部类实现)

public class Holder{
	private Holder(){
	
	}
	public static Holder getInstace(){
		return InnerClass.HOLDER;
	}
	public static class InnerClass{
		private static final Holder HOLDER = new Holder();
	}
}

但以上这些懒汉式其实都是不安全的,因为反射可以破坏单例模式

public class LazyMan{
	private LazyMan(){
	}
	private volatile static LazyMan lazyMan;
	//双重检测锁
	public static LazyMan getInstance(){
		if(lazyMan==null){
			synchronized (LazyMan.class){
					if(lazyMan==null){
						lazyMan=new LazyMan();
					}
			}
		}
		return lazyMan;
	}
	//反射
	public static void main (String[] args){
		LazyMan instance1=LazyMan.getInstance();
		Constructor<LazyMan> declaredConstructor=LazyMan.class.getDeclaredConstructor(null)
		//破坏构造器私有化
		declaredConstructor.setAccessible(true);
		LazyMan instance2 =declaredConstructor.newInstance();
		//打印的值并不相同,说明单例已被破坏
		System.out.println(instance1);
		System.out.println(instance2);
}

当然这种也有解决的方法

public class LazyMan{
	private LazyMan(){
		synchronized(LazyMan.class){
			if(lazyMan!=null){
				throw new RuntimeException("请不要使用反射破坏单例异常"}
	private volatile static LazyMan lazyMan;
	//双重检测锁
	public static LazyMan getInstance(){
		if(lazyMan==null){
			synchronized (LazyMan.class){
					if(lazyMan==null){
						lazyMan=new LazyMan();
					}
			}
		}
		return lazyMan;
	}
	//反射
	public static void main (String[] args){
		LazyMan instance1=LazyMan.getInstance();
		Constructor<LazyMan> declaredConstructor=LazyMan.class.getDeclaredConstructor(null)
		//破坏构造器私有化
		declaredConstructor.setAccessible(true);
		LazyMan instance2 =declaredConstructor.newInstance();
		//打印时报错
		System.out.println(instance1);
		System.out.println(instance2);
}

这时再次执行main方法时会报错运行时异常
如果将两个instance对象都直接new出来

public class LazyMan{
	private LazyMan(){
		synchronized(LazyMan.class){
			if(lazyMan!=null){
				throw new RuntimeException("请不要使用反射破坏单例异常"}
	private volatile static LazyMan lazyMan;
	//双重检测锁
	public static LazyMan getInstance(){
		if(lazyMan==null){
			synchronized (LazyMan.class){
					if(lazyMan==null){
						lazyMan=new LazyMan();
					}
			}
		}
		return lazyMan;
	}
	//反射
	public static void main (String[] args){
		//LazyMan instance1=LazyMan.getInstance();
		Constructor<LazyMan> declaredConstructor=LazyMan.class.getDeclaredConstructor(null)
		//破坏构造器私有化
		declaredConstructor.setAccessible(true);
		LazyMan instance1 =declaredConstructor.newInstance();
		LazyMan instance2 =declaredConstructor.newInstance();
		//打印时打印不同的地址-->单例再次被破坏
		System.out.println(instance1);
		System.out.println(instance2);
}

我们发现单例依然可以被破坏
此时我们再次进行升级定义一个假设其他人都不知道的变量

public class LazyMan{
	//定义参数,也可以说密钥
	private static boolean flag=false;
	private LazyMan(){
		synchronized(LazyMan.class){
			if(flag==fasle){
				flag=true;
			}else{
				throw new RuntimeException("请不要使用反射破坏单例异常"}
		}
	private volatile static LazyMan lazyMan;
	//双重检测锁
	public static LazyMan getInstance(){
		if(lazyMan==null){
			synchronized (LazyMan.class){
					if(lazyMan==null){
						lazyMan=new LazyMan();
					}
			}
		}
		return lazyMan;
	}
	//反射
	public static void main (String[] args){
		//LazyMan instance1=LazyMan.getInstance();
		Constructor<LazyMan> declaredConstructor=LazyMan.class.getDeclaredConstructor(null)
		//破坏构造器私有化
		declaredConstructor.setAccessible(true);
		LazyMan instance1 =declaredConstructor.newInstance();
		LazyMan instance2 =declaredConstructor.newInstance();
		//打印时抛出异常
		System.out.println(instance1);
		System.out.println(instance2);
}

这时我们发现好像单例不会被破坏了,但是如果有人破解了flag字段,使得flag字段私有化失效

public class LazyMan{
	//定义参数,也可以说密钥
	private static boolean flag=false;
	private LazyMan(){
		synchronized(LazyMan.class){
			if(flag==fasle){
				flag=true;
			}else{
				throw new RuntimeException("请不要使用反射破坏单例异常"}
		}
	private volatile static LazyMan lazyMan;
	//双重检测锁
	public static LazyMan getInstance(){
		if(lazyMan==null){
			synchronized (LazyMan.class){
					if(lazyMan==null){
						lazyMan=new LazyMan();
					}
			}
		}
		return lazyMan;
	}
	//反射
	public static void main (String[] args){
		//LazyMan instance1=LazyMan.getInstance();
		Field flag =LazyMan.class.getDeclaredField("flag")
		//使得私有化参数失效
		flag.setAccessible(true);
		Constructor<LazyMan> declaredConstructor=LazyMan.class.getDeclaredConstructor(null)
		//破坏构造器私有化
		declaredConstructor.setAccessible(true);
		LazyMan instance1 =declaredConstructor.newInstance();
		//更改flag值为false
		flag.set(instance1,false);
		LazyMan instance2 =declaredConstructor.newInstance();
		//打印时抛出异常
		System.out.println(instance1);
		System.out.println(instance2);
}

破解了flag字段后我们发现单例依然可以被破坏
这时候就出现了枚举类单例模式

6.懒汉式单例(枚举实现)

public enum EnumSingle{
	INSTANCE;
	public EnumSingle getInstance(){
		return INSTANCE;
	}
	class Test{
		public static void main (String[] args){
			EnumSingle instance1 = EnumSingle.INSTANCE;
			EnumSingle instance2 = EnumSingle.INSTANCE;
			//打印的值为相同的
			System.out.println(instance1);
			System.out.println(instance2);
}

这时我们再次通过反射去破坏单例

public enum EnumSingle{
	INSTANCE;
	public EnumSingle getInstance(){
		return 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.new   Instance()
			//报错枚举类中没有空参的构造方法
			System.out.println(instance1);
			System.out.println(instance2);
}

通过jad工具反编译
发现构造器为有参构造参数为(String s,int i)

public enum EnumSingle{
	INSTANCE;
	public EnumSingle getInstance(){
		return INSTANCE;
	}
	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.new   Instance()
			//报错不能用反射破坏单例
			System.out.println(instance1);
			System.out.println(instance2);
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值