一步到位的单例模式

定义:保证一个雷只有一个实例,并且提供一个全局访问点

场景:线程池、数据库连接池等

实现方式:

1、懒汉模式

      1)保证线程安全

      2)双重检查重排

      3)防止指令重排

public class Singleton {

	private static Singleton instance;
	// 1、私有构造方法
	private Singleton(){
	}
	//2、 提供共有的调用方法
	public static Singleton getIntance(){
		
		if(instance==null){
			synchronized (Singleton.class) {
				if(instance==null){
					instance = new Singleton();
				}
			}
		}
		return instance;
	}
}

 这个已经加锁了,虽然没有线程安全问题了,但是还是有问题就是JVM在初始化的时候,当执行到【new Singleton();】时,对class使用javap 可以反编译看到,对instance进行赋值的时候一共分为三步,1、分配空间,返回一个指向性该空间的内存引用,2、把内存引用赋值给instance,3、对空间进行初始化。当执行到第二步完成时,还没有初始化空间,有一个线程进来发现instance不为空,直接返回instance。就会存在空的报错。

解决办法:

private volatile static Singleton instance;

就是在定义的时候加上volatile关键字,防止jvm、cpu等对它重排

2、饿汉模式:在类加载阶段完成实例的初始化。通过类加载机制,来保证我们的线程安全。

public class HungrySingleton {

	//1、进行实例化
	private static HungrySingleton instance = new HungrySingleton();
	//2、提供共有的调用方法
	public static HungrySingleton getHungrySingleton(){
		return instance;
	}
	//3、私有化构造方法
	private HungrySingleton(){
		
	}
}

   对于恶汉模式可以使用内部类实现延迟加载

public class HungrySingleton {

	// 1、私有化构造方法
	private HungrySingleton() {

	}

	// 3、提供共有的调用方法
	public static HungrySingleton getHungrySingleton() {
		return InerClass.instance;
	}

	static class InerClass {
		// 2、进行实例化
		private static HungrySingleton instance = new HungrySingleton();
	}
}

这个看似完美了,但是还是有一个问题就是当用反射的创建的时候,还是可以重新创建一个实例,为了防止反射创建一个新的实例

代码如下

public class HungrySingleton {

	// 1、私有化构造方法
	private HungrySingleton() {
		if(InerClass.instance != null){
			throw new RuntimeException("已经创建了一个实例,别想用反射再来一遍");
		}
	}

	// 3、提供共有的调用方法
	public static HungrySingleton getHungrySingleton() {
		return InerClass.instance;
	}

	static class InerClass {
		// 2、进行实例化
		private static HungrySingleton instance = new HungrySingleton();
	}
}

这个虽然解决了反射的问题,但是还有一个序列化和反序列化的问题需要解决,反序列化后,修改属性后还是会有问题

修改如下

import java.io.ObjectStreamException;
import java.io.Serializable;


public class HungrySingleton implements Serializable{

	private static HungrySingleton instance = new HungrySingleton();
	private HungrySingleton() {
		if(instance != null){
			throw new RuntimeException("已经创建了一个实例,别想用反射再来一遍");
		}
	}

	public static HungrySingleton getHungrySingleton() {
		return instance;
	}
	Object readResolve() throws ObjectStreamException{
		return instance;
	}
}

这样就完美解决了关于序列化和反序列化的签名不一致的问题

3、枚举类型

枚举类型中已经解决了反序列化和反射的问题

public enum EnumSingleton {

	INSTANCE;
}

枚举类型的就是这么简单明了

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值