单例模式

单例简介

* 特点:
    * 至多有一个实例;
    * 提供全局访问点;
    * 是创建型设计模式;

* 应用:
    * J2EE 标准中,ServletContext、ServletContextConfig;
    * spring 框架应用中 ApplicationContext, 数据库的连接池;

饿汉式

简介
* 特点:
    * 在类加载时创建单例对象;
        * 类加载时间:
            * 同一包下第一次调用;
            * 非同一包下第一次import;
    * 绝对线程安全;
        * 类加载机制是jvm提供的, 线程安全;
        * 类第一次加载时, 其他使用该类的线程处于阻塞状态, 直到类加载完成, 该类可被使用;

* 优点:
    * 开发简单,线程安全;
    * 不加锁执行效率高;
* 缺点:
    * 类加载时就初始化, 可能浪费内存空间;
    * 所以当需要单例的对象较多时, 不适用饿汉式;

* 类加载顺序:
    * 先静态,后动态;
    * 先属性, 后方法;
    * 先上后下
CODE
思路
饿汉式实现:
	1.静态成员变量方式;
	2.静态代码块方式;
CODE
  • 实现
public class HungrydSingleton {

	private HungrydSingleton(){
		
	}
	
	//静态变量
	public final static HungrydSingleton singletonV1 = new HungrydSingleton();
	public static HungrydSingleton getInstanceV1(){
		return singletonV1;
	}
	
	//静态代码块
	public static HungrydSingleton singletonV2 = null;
	static{
		singletonV2 = new HungrydSingleton();
	}
	public static HungrydSingleton getInstanceV2(){
		return singletonV2;
	}
}

懒汉式

简介
* 特点:
	* 被调用的时, 才会创建单例对象;
	* 在未被调用的情况下, 比饿汉式节约内存, 但是不被调用创建还有什么意义呢?

代码
思路
* 饿汉式实现方式有三种:
1.同步代码块;
2.doubleCheck
3.静态内部类;
code
public class LazySingleton {

	private LazySingleton(){

	}

	//基本实现, 存在线程安全问题;
	public static LazySingleton singletonV1;
	public static LazySingleton getInstanceV1(){
		if(singletonV1 == null){
			singletonV1 = new LazySingleton();
		}
		return singletonV1;
	}
	
	
	//同步代码块, 解决线程安全问题, 效率较低;
	public static LazySingleton singletonV2;
	public synchronized static LazySingleton getInstanceV2(){
		if(singletonV2 == null){
			singletonV2 = new LazySingleton();
		}
		return singletonV2;
	}
	
	//doubleCheck 线程安全, 效率高;
	public static LazySingleton singletonV3;
	public  static LazySingleton getInstanceV3(){
		if(singletonV3 == null){
			synchronized (LazySingleton.class) {
				if(singletonV3 == null){
					singletonV3 = new LazySingleton();
				}
			}
		}
		return singletonV3;
	}

	//静态内部类, 当调用时加载, 且内部类采用懒汉式创建对象线程安全;
	static class innerClass {
		public static  final LazySingleton singletonV4 = new LazySingleton();
	}
	public  static LazySingleton getInstanceV4(){
		return innerClass.singletonV4;
	}
}

注册式

简介
* 特点:
	* 把需要单例的实例存放在容器中(在容器中注册);

* 应用:
	* ThreadLocal:
		* 采用注册式单例模式, 单挑线程中对象是单例的;
		* 容器: CurrentHashMap<String,Object>
			* key : 线程名;
			* Object: 单例对象;

* 枚举本身防单例破坏: 见单例破坏部分;
代码
思路
* 注册时单例最典型的应用是枚举:
	* 枚举本身不对外提供构造方法;
	* 枚举线程安全;
		* 原因: 见下文单例破坏部分;

* 使用其他容器, 需要考虑校验非空时, 线程安全问题;
CODE
  • 枚举
public enum EnumSingleton {
    INSTANCE;
    private Object data;

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public static EnumSingleton getInstance() {
        return INSTANCE;
    }
}

单例破坏及防止;

简介
  • 破坏方法
1. 反射技术破坏
2. 序列化技术破坏
  • 反射破坏防止:
* Constructor类调用构造器时, 抛异常;
* 存在单例对象时, 调用构造器抛异常;

* 枚举:
	* 枚举本身支持防止反射破坏的功能;
	* 阅读Constracter中newInstance()源码可发现, 如果class类型是枚举, 那么直接抛出异常, 说明枚举不能使用构造器创建实例;
  • 序列化破坏防止:
* 添加readResolve()方法, 方法返回单例对象;
* 枚举本身具备防止序列化破坏的功能;

* 原理:
	* 阅读ObjectInputStream中readObject()源码:
		* ObjectInputStream为checkResolve(Object obj)根据数据类型的不同, 做了多次重载;
			* 其中包括: String/Array/Class/Exception/Enum/Object;
		* 一般对象: checkResolve(readOrdinaryObject(unshared));
			1. 读取序列化字节并创建对象obj;
			2. 判断是否有readResolve()方法, 如果有, 则obj指向readResolve()返回的对象;
			3. 返回obj;
		* 枚举: checkResolve(readEnum(unshared));
			1. 读取序列化字节并创建对象obj;
			2. Enum.valueOf(clazz, name); 获取枚举已经存在的单例对象;
				* 如果获取到, 则obj指向该枚举的单例对象;
				* 如果没获取到, 则直接抛异常;
			3.返回obj;

代码
  • 防止破坏
public class BreakSingleton implements Serializable{


	private static final long serialVersionUID = 1L;
	
	//防止反射破坏
	private BreakSingleton(){
		if(singleton != null){
			throw new RuntimeException("singleton can not create by constract");
		}
		
		//不允许构造器创建;
		 StackTraceElement stack[] = Thread.currentThread().getStackTrace();  
		 for (StackTraceElement ste:stack){
			 if(ste.getClassName().equals("java.lang.reflect.Constructor")){
				 throw new RuntimeException("singleton can not create by constract!");
			 }
		 }
	}
	
	
	//防止序列化破坏
	private Object readResolve() {
        return singleton;
    }
	
	
	private static BreakSingleton singleton;
	public static BreakSingleton getInstance(){
		if(singleton == null){
			synchronized (BreakSingleton.class) {
				if(singleton == null){
					singleton = new BreakSingleton();
				}
			}
		}
		
		return singleton;
	}
}
  • 测试
public static void main(String[] args) throws Exception {
		
		Class<?> clazz = BreakSingleton.class;
		Constructor<?> c = clazz.getDeclaredConstructor();
		c.setAccessible(true);
		BreakSingleton bs = (BreakSingleton) c.newInstance();
		
		
		BreakSingleton singleton =  BreakSingleton.getInstance();
		String filename = "d:\\abc";
		
		FileOutputStream fos = new FileOutputStream(filename);
		ObjectOutputStream oos = new ObjectOutputStream(fos);
		oos.writeObject(singleton);
		oos.flush();
		oos.close();
		
		FileInputStream fis = new FileInputStream(filename);
		ObjectInputStream ois = new ObjectInputStream(fis);
		BreakSingleton singletonV2 = (BreakSingleton) ois.readObject();
		ois.close();
		
		System.out.println(singleton == singletonV2);
		System.out.println(bs == singleton);
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值