设计模式--单例模式
单例模式
定义:保证一个类只有一个实例,并且提供一个全局访问点
场景:重量级的对象,不需要多个实例,如线程池,数据库连接池
1) 懒汉模式:延迟加载,只有在真正使用的时候,才开始实例化
a.线程安全问题
b.Double check 加锁优化
c.编译器(JIT),CPU有可能对指令进行重排序,导致使用到尚未初始化的实例,可以通过添加volatile关键字进行修饰,对于volatile修饰的字段可以防止指令重排。
代码:
package com.offcn.designpattern.lazysingleton; //单例模式-懒汉模式 public class LazysingletonDemo { public static void main(String[] args) { new Thread(()->{ LazySingleton instance = LazySingleton.getInstance(); System.out.println(instance); }).start(); new Thread(()->{ LazySingleton instance = LazySingleton.getInstance(); System.out.println(instance); }).start(); } } class LazySingleton{ private volatile static LazySingleton instance; private LazySingleton(){
//是为了避免反射创建其他实例
if(instance != null){
throw RuntimeException("单例不允许创建多个实例")
}
} public static LazySingleton getInstance(){ if(instance == null){ synchronized (LazySingleton.class){ if(instance == null){ instance = new LazySingleton(); //字节码层,2.3顺序可以调,但是可能会引发空指针,所以用volatile //1.分配空间 //2.初始化 //3.引用赋值 } } } return instance; } }
输出:

2)饿汉模式:类加载的初始化阶段就完成了实例的初始化,本质上就是借助于jvm类的加载机制,保证实例的唯一性。
类加载过程:
a.加载二进制数据到内存中,生成对应的Class数据结构。
b.连接:A验证B准备(给类的静态成员变量赋默认值)C解析
c.初始化:给类的静态变量赋初值
package com.offcn.designpattern.lazysingleton; public class HungarysingletonDemo { public static void main(String[] args) { HungarySingleton instance = HungarySingleton.getInstance(); HungarySingleton instance1 = HungarySingleton.getInstance(); System.out.println(instance == instance1); } } class HungarySingleton{ private static HungarySingleton instance = new HungarySingleton(); private HungarySingleton(){} public static HungarySingleton getInstance(){ return instance; } }
3) 静态内部类
a.本质上是利用类的加载机制来保证线程安全
b.只有在实际使用的时候才会出发类的初始化,所以也是懒加载的一种形式
package com.offcn.designpattern.lazysingleton; public class InnerclassSingletonDemo { public static void main(String[] args) { new Thread(()->{ InnerclassSingleton instance = InnerclassSingleton.getInstance(); System.out.println(instance); }).start(); new Thread(()->{ InnerclassSingleton instance1 = InnerclassSingleton.getInstance(); System.out.println(instance1); }).start(); } } //静态内部类 class InnerclassSingleton{ private static class InnerclassHolder{ private static InnerclassSingleton instance = new InnerclassSingleton(); } private InnerclassSingleton(){
//是为了避免反射创建其他实例
if(intance != null){
throw RuntimeException("单例模式不允许创建多个实例");
}
} public static InnerclassSingleton getInstance(){ return InnerclassHolder.instance; } }
输出:

4) 使用反射攻击懒加载式单例模式
package com.offcn.designpattern.lazysingleton; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class InnerclassSingletonDemo { public static void main(String[] args)throws Exception { //通过反射得到调用构造方法创建实例 Constructor<InnerclassSingleton> declaredConstructor = InnerclassSingleton.class.getDeclaredConstructor(); declaredConstructor.setAccessible(true); InnerclassSingleton innerclassSingleton = declaredConstructor.newInstance(); //通过静态内部类得到实例 InnerclassSingleton instance = InnerclassSingleton.getInstance(); //判断得到的是否是相同实例,结果返回false System.out.println(innerclassSingleton == instance); } } //静态内部类 class InnerclassSingleton{ private static class InnerclassHolder{ private static InnerclassSingleton instance = new InnerclassSingleton(); } private InnerclassSingleton(){} public static InnerclassSingleton getInstance(){ return InnerclassHolder.instance; } }
解决方法:

5 ) 使用序列化攻击单例模式(因为序列化会调用流来创建实例)
package com.offcn.designpattern.lazysingleton;import ,java.io.*;
import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class InnerclassSingletonDemo { public static void main(String[] args)throws Exception { InnerclassSingleton instance = InnerclassSingleton.getInstance(); //序列化 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("innerclassSingleton")); oos.writeObject(instance); oos.close(); //反序列化 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("innerclassSingleton")); InnerclassSingleton instance1 = (InnerclassSingleton) ois.readObject();
//返回false System.out.println(instance == instance1); } } //静态内部类 class InnerclassSingleton implements Serializable { private static class InnerclassHolder{ private static InnerclassSingleton instance = new InnerclassSingleton(); } private InnerclassSingleton(){} public static InnerclassSingleton getInstance(){ return InnerclassHolder.instance; } }
解决方法:
class InnerclassSingleton implements Serializable {
//固定版本号 static final long serialVersionUID = 42L; private static class InnerclassHolder{ private static InnerclassSingleton instance = new InnerclassSingleton(); } private InnerclassSingleton(){} public static InnerclassSingleton getInstance(){ return InnerclassHolder.instance; } Object readResolve()throws ObjectStreamException{ return InnerclassHolder.instance; } }
6 ) 枚举创建单例
package com.offcn.designpattern.lazysingleton; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public enum EnumSingleton { INSTANCE; } class EnumTest{ public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException { EnumSingleton instance = EnumSingleton.INSTANCE; EnumSingleton instance1 = EnumSingleton.INSTANCE; //返回true System.out.println(instance == instance1); // 反射不能在枚举类型使用,会报异常 Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor(); declaredConstructor.setAccessible(true); EnumSingleton enumSingleton = declaredConstructor.newInstance("INSTANCE",0); } }
反射在枚举中使用时会报异常:

序列化在枚举中使用不会创建多个实例
public enum EnumSingleton { INSTANCE; } class EnumTest{ public static void main(String[] args) throws IllegalAccessException, InvocationTargetException,
InstantiationException, NoSuchMethodException, IOException, ClassNotFoundException { EnumSingleton instance = EnumSingleton.INSTANCE; //序列化 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("enumsingleton")); oos.writeObject(instance); oos.close(); //反序列化 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("enumsingleton")); EnumSingleton instance1 = (EnumSingleton) ois.readObject(); //在枚举中使用序列化,不会创建多个实例 //返回true System.out.println(instance == instance1); } }
。

浙公网安备 33010602011771号