Java单例模式
gonghr 人气:0饿汉式
饿汉式:类加载就会导致该单实例对象被创建
// 问题1:为什么加 final // 问题2:如果实现了序列化接口, 还要做什么来防止反序列化破坏单例 public final class Singleton_hungry implements Serializable { // 问题3:为什么设置为私有? 是否能防止反射创建新的实例? private Singleton_hungry(){} // 问题4:这样初始化是否能保证单例对象创建时的线程安全? private static Singleton_hungry INSTANCE = new Singleton_hungry(); // 问题5:为什么提供静态方法而不是直接将 INSTANCE 设置为 public, 说出你知道的理由 public static Singleton_hungry getInstance() { return INSTANCE; } public Object readResolve(){ // 防止反射创建新的实例? return INSTANCE; } }
- 问题1:避免子类覆盖父类的一些方法,导致线程不安全。
- 问题2:实现
readResolve
方法。当从对象流ObjectInputStream
中读取对象时,会检查对象的类否定义了readResolve
方法。如果定义了,则调用它返回我们想指定的对象(这里就指定了返回单例对象)。 - 问题3:防止通过
new
创建对象实例。不能防止反射创建新的实例。 - 问题4:可以。静态变量初始化在类加载时进行,由
jvm
进行管理,可以保证线程安全。 - 问题5:通过方法,可以提高拓展性,改进饿汉式转化为懒汉式、利用泛型特性、增加对单例对象的控制操作。
枚举单例
enum Singleton { INSTANCE; }
问题1:枚举单例是如何限制实例个数的
单例相当于枚举的静态成员变量,定义几个就有几个实例。
问题2:枚举单例在创建时是否有并发问题
单例相当于枚举的静态成员变量,类加载时初始化,由 jvm
进行管理,可以保证线程安全。
问题3:枚举单例能否被反射破坏单例
不能
问题4:枚举单例能否被反序列化破坏单例
枚举实现了 Serializable
接口,可序列化,但不会被反序列破坏单例。
问题5:枚举单例属于懒汉式还是饿汉式
饿汉式
问题6:枚举单例如果希望加入一些单例创建时的初始化逻辑该如何做
枚举允许构造方法
懒汉式
public final class Singleton_lazy { private Singleton_lazy(){} private static Singleton_lazy INSTANCE = null; // 缺点 public static synchronized Singleton_lazy getInstance() { if(INSTANCE != null) { return INSTANCE; } INSTANCE = new Singleton_lazy(); return INSTANCE; } }
synchronized
保证线程安全,但锁粒度较大,性能低。
DCL 懒汉式
public final class Singleton_DCL { private Singleton_DCL() {} // 问题1:解释为什么要加 volatile ? private static volatile Singleton_DCL INSTANCE= null; // 问题2:对比实现3, 说出这样做的意义 public static Singleton_DCL getInstance() { if(INSTANCE != null) { return INSTANCE; } synchronized (Singleton_DCL.class) { // 问题3:为什么还要在这里加为空判断, 之前不是判断过了吗 if(INSTANCE != null) { return INSTANCE; } INSTANCE = new Singleton_DCL(); return INSTANCE; } } }
问题1:避免指令重排序,导致赋值语句先于构造函数执行,得到一个未初始化完毕的对象。
问题2、3:Double Check Lock
机制。同步代码块外部的判断语句主要用于 INSTANCE
初始化并赋值之后,此时 INSTANCE != null
,如果有多个线程尝试获取单例,可以提前返回,不用执行同步代码块。而同步代码块内部的判断主要用于第一次初始化时,INSTANCE = null
,此时可以有多个线程尝试获取 INSTANCE
,只能有一个线程进入同步代码块,其他线程在同步代码块外阻塞,该线程创建一个单例对象之后,唤醒其他线程,再进入同步代码块,发现 INSTANCE != null
,则直接返回,不用重新创建单例对象,提高了效率。
静态内部类懒汉单例
public final class Singleton_LazyHolder { private Singleton_LazyHolder(){} // 问题1:属于懒汉式还是饿汉式 private static class LazyHolder{ static final Singleton_LazyHolder INSTANCE = new Singleton_LazyHolder(); } // 问题2:在创建时是否有并发问题 public static Singleton_LazyHolder getInstance() { return LazyHolder.INSTANCE; } }
问题1:懒汉式。静态内部类只有在被方法调用的时候才进行初始化,类加载。
问题2:无,类加载由 jvm
进行,线程安全。
加载全部内容