单例模式

    技术2022-07-10  136

    单例模式

    简介单例模式的写法饿汉模式懒汉模式静态内部类单例模式枚举单例模式

    简介

    保证一个类仅有一个实例,并提供一个访问它的全局访问点。

    单例模式的写法

    饿汉模式

    线程安全,通过JVM的类加载机制保证其只会有一个实例。写法示例 package designpattren.hungrysingleton; /** * 饿汉单例模式 */ public class HungrySingletonTest { public static void main(String[] args) { HungrySingleton instance = HungrySingleton.getInstance(); HungrySingleton instance1 = HungrySingleton.getInstance(); System.out.println(instance == instance1); } } /** * 饿汉单例模式,这种方式是线程安全的,通过JVM的类加载机制可以保证每个类只会被加载一次 * 类加载过程: * 1、加载二进制数据到内存中,生成对应的CLass数据结构 * 2、连接:a、验证 b、准备,给类的静态成员赋默认值,c、解析 * 3、初始化:给类的静态变量赋初值 * */ class HungrySingleton{ private static HungrySingleton instance =new HungrySingleton(); private HungrySingleton(){ } public static HungrySingleton getInstance(){ return instance; } }

    懒汉模式

    线程不安全,用volatile关键字,防止JIT进行指令重排,导致的空指针问题,使得其可以在多线程环境下使用。写法示例 package designpattren.lazysingleton; /** * 懒汉单例模式 */ public class LazySingletonTest { public static void main(String[] args) { // LazySingleTon instance = LazySingleTon.getInstance(); // LazySingleTon instance1 = LazySingleTon.getInstance(); // System.out.println(instance == instance1); new Thread(()->{ LazySingleTon instance =LazySingleTon.getInstance(); System.out.println(instance); }).start(); new Thread(()->{ LazySingleTon instance =LazySingleTon.getInstance(); System.out.println(instance); }).start(); } } class LazySingleTon{ //使用volatile关键字,防止JIT进行指令重排,导致的空指针问题 private volatile static LazySingleTon instance; private LazySingleTon(){ } //全局访问点 public static LazySingleTon getInstance(){ if (instance ==null){ synchronized (LazySingleTon.class){ if(instance==null){ instance = new LazySingleTon(); /** * JIT,CPU可能会对2.3进行重排 * 字节码层 * 1、分配空间 * 2、初始化 * 3、引用赋值 */ } } } return instance; } }

    静态内部类单例模式

    线程安全,通过静态内部类实现单例,同样依赖JVM的类加载机制保证它的线程安全。写法示例 package designpattren.innerclasssingleton; import java.io.*; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class InnerClassSingletonTest { public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException, ClassNotFoundException { // 对Instance进行序列化 InnerClassSingleton instance = InnerClassSingleton.getInstance(); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("testSerializable")); oos.writeObject(instance); oos.close(); //反序列化 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("testSerializable")); Object object =(InnerClassSingleton)ois.readObject(); System.out.println(instance == object); } } /** * 基于静态内部类实现单例模式 * 依赖JVM的类加载机制保证线程安全 */ class InnerClassSingleton implements Serializable { static final long serialVersionUID = 42L; private static class InnerClassHolder{ private static InnerClassSingleton instance = new InnerClassSingleton(); } private InnerClassSingleton(){ if(InnerClassHolder.instance!=null){ throw new RuntimeException("单例不允许多个实例"); } } public static InnerClassSingleton getInstance(){ return InnerClassHolder.instance; } Object readResolve() throws ObjectStreamException{ return InnerClassHolder.instance; } }

    枚举单例模式

    使用枚举实现的单例模式也是线程安全的,写法也比较简单,缺点是可读性比较低。这种方式下,也可以防止序列化攻击。写法示例 package designpattren.enumsingleton; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Constructor; public enum EnumSingleton{ INSTANCE; public void print(){ System.out.println(this.hashCode()); } } class EnumTest{ public static void main(String[] args) throws Exception { //Enum不支持反射,线程安全 // Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor(String.class, Integer.class); // declaredConstructor.setAccessible(true); // declaredConstructor.newInstance("INSTANCE",0); EnumSingleton instance = EnumSingleton.INSTANCE; // ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test EnumSingleton")); // oos.writeObject(instance); // oos.close(); //反序列化 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test EnumSingleton")); Object object =( EnumSingleton)ois.readObject(); System.out.println(object == instance); } }
    Processed: 0.011, SQL: 9