饿汉式
- 空间浪费
- 多线程安全
- 可能会被破坏
/**
* 饿汉式,单例模式
* 会存在空间的浪费(当还不需要单例时)
* 多线程下是安全的
*/
class HungrySingle {
private final static HungrySingle hs = new HungrySingle();
private HungrySingle() {
}
public static HungrySingle getInstance() {
return hs;
}
}
懒汉式
- 多线程不安全
- 可能会被破坏
/**
* 懒汉式单例设计模式
* 多线程下不安全
* 只有使用时,才会分配内存空间
*/
class LazySingle {
private static LazySingle ls;
private LazySingle() {
}
public static LazySingle getInstance() {
if (ls != null) {
ls = new LazySingle();
}
return ls;
}
}
DCL懒汉式
- 多线程下是安全的
- 不会造成指令重排
- 双重锁
/**
* DCL懒汉设计模式
* 这个可能会被破坏
* 懒汉设计模式提升版
* 多线程下是安全的
* 没有指令重排的安全隐患
* 为什么要使用volatile关键字,防止指令重排
* 正常分配空间的步骤
* 1.分配内存空间
* 2.执行构造方法,初始化对象
* 3.把这个对象指向这个空间
* 如果发生了指令重排 132
* 当这时另一个线程在指令重排3结束时2还未开始时访问了单例,虽然单例已经不为空,但是还没有完成初始化
* 就会产生异常
*/
class LazySingleImpro {
private volatile static LazySingleImpro ls;
private LazySingleImpro() {
}
public static LazySingleImpro getInstance() {
if (ls == null) {
synchronized (LazySingleImpro.class){
if(ls==null){
ls= new LazySingleImpro();
}
}
}
return ls;
}
}
使用枚举创建单例
问题
不论是饿汉式还是懒汉式的单例设计模式
懒汉式的单例设计模式尽管使用了双重检查锁
但是依旧无法阻挡单例模式的破坏
使用反射和序列化就可以破环饿汉式和懒汉式的单例
使用反射破坏懒汉式的单例
import java.lang.reflect.Constructor;
/**
* @ClassName: SingletonClient
* @Author: 楠
* @Description: TODO
*/
public class SingletonClient {//这里的懒汉式的单例,又重新写了一个所以名字和上面是不一样的
public static void main(String[] args) throws Exception{
System.out.println("使用双重检查锁,懒汉式创建单例");
LazySingleton instence = LazySingleton.getInstence();
LazySingleton instence1 = LazySingleton.getInstence();
System.out.println(instence);
System.out.println(instence1);
System.out.println();
System.out.println("使用反射破环单例");
Class<?> clazz = Class.forName("singleton.LazySingleton");
Constructor<?> declaredConstructor = clazz.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Object o = declaredConstructor.newInstance();
Object o1 = declaredConstructor.newInstance();
System.out.println(o);
System.out.println(o1);
System.out.println("可以发现使用反射确实可以破环饿汉和懒汉式的单例");
System.out.println();
System.out.println("使用枚举创建单例");
EnumSingleton instance = EnumSingleton.getInstance();
EnumSingleton instance1 = EnumSingleton.getInstance();
System.out.println(instance);
System.out.println(instance1);
}
}
使用枚举创建单例
枚举是不允许使用反射创建对象的,使用反射创建枚举对象是会抛出异常
/**
* @ClassName: EnumSingleton
* @Author: 楠
* @Description: 使用枚举创建单例
*/
public class EnumSingleton {
//私有化构造函数
private EnumSingleton (){
}
public static EnumSingleton getInstance(){
return ContainerHolder.HOLDER.instance;
}
private enum ContainerHolder{
HOLDER;
private EnumSingleton instance;
ContainerHolder(){
instance=new EnumSingleton();
}
}
}