单例模式详解

概述

  • 确保单例类只能创建自己的唯一实例,并向外提供该实例
  • 唯一实例节省系统创建销毁实例时间,减少内存开销

饿汉式(类加载时实例化)

1.代码实现

public class Hungry {
    private static Hungry instance = new Hungry();//线程安全
    private Hungry() {}
    public static Hungry getInstance() {
        return instance;
    }
}

2.缺点

  • 可能造成空间浪费(一上来就加载这个类),所以出现懒汉式
    在这里插入图片描述

懒汉式(使用时实例化)

1.普通懒汉式

class Singleton{
	private static Singleton instance;
	private Singleton(){}
	public synchronized static Singleton getInstance(){//加锁:防止多线程情况下被多次new——加锁线程安全
		if(instance==null){
			instance = new Singleton();
		}
		return instance;
	}
}

2.双重检查锁单例模式

class Singleton{
	private volatile static Singleton instance;
	/**
	 * volatile关键字避免重排序
	 * instance = new Singleton();在底层有三步操作,不是原子性操作
	 * 1.分配内存空间
	 * 2.执行构造方法,初始化对象
	 * 3.将对象指向该内存空间
	 * CPU执行顺序不完全按照123的顺序,volatile可以保证有序性,防止指令重排序
	 */
	private Singleton(){}
	public static Singleton getInstance(){//减小锁的范围
		if(instance==null){
			synchronized(Singleton.class){
				if(instance==null){
					instance = new Singleton();
				}
			}
		}
		return instance;
	}
}

静态内部类

class Singleton{
	private Singleton(){}
	
	public static class InnerClass{
		private static final Singleton instance = new Singleton();
	}
	
	public static Singleton getInstance(){
		return InnerClass.instance;
	}
}

解决反射破坏单例模式问题

  • 通过反射可以破环以上所有的安全性
  • 破环双重检查锁单例模式为例
class Singleton{
	private volatile static Singleton instance;
	private Singleton(){}
	public static Singleton getInstance(){
		if(instance==null){
			synchronized(Singleton.class){
				if(instance==null){
					instance = new Singleton();
				}
			}
		}
		return instance;
	}
}

1.问题1

  • 问题描述:分别两次获取单例,第一次通过getInstance获取,第二次通过反射newInstance获取
  • 解决:在构造方法中再一层锁进行判断
class Singleton{
	private volatile static Singleton instance;
	
	private Singleton(){
		synchronized(Singleton.class){
			if(instance!=null){
				throw new RuntimeException("不要试图通过反射破坏单例模式");
			}
		}
	}
	
	public static Singleton getInstance(){
		if(instance==null){
			synchronized(Singleton.class){
				if(instance==null){
					instance = new Singleton();
				}
			}
		}
		return instance;
	}
}
  • 缺陷:两次获取实例都通过反射获取怎么办,请看问题2

2.问题2

  • 问题描述:分别两次获取单例,都通过反射newInstance获取,问题1的解决办法就失效了
  • 解决:在类中加一个标志变量
class Singleton{
	private volatile static Singleton instance;
	
	private static boolean flag = false;
	private Singleton(){
		synchronized(Singleton.class){
			if(flag==false){
				flag = true;
			}else{
				throw new RuntimeException("不要试图通过反射破坏单例模式");
			}
		}
	}
	
	public static Singleton getInstance(){
		if(instance==null){
			synchronized(Singleton.class){
				if(instance==null){
					instance = new Singleton();
				}
			}
		}
		return instance;
	}
}
  • 缺陷:flag通过反射被改怎么办,请看问题3

3.问题3

  • 通过以上发现总是可以通过反射破环单例模式的安全性,阅读源码发现枚举类型不允许被反射破坏
    在这里插入图片描述
  • 到这里往下看枚举知识(重点啊)

枚举

1.概述

  • 枚举本身就是类,加上enum关键字

2.反射到底可不可以破环枚举呢(重点)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值