一、单例模式 - 6种实现
一、单例模式
特点:
-
单例类只能有一个实例
-
单例类必须自己创建自己的唯一实例
-
单例类必须给所有其他对象提供这一实例
解决问题:
1. 懒汉模式
public class SingletonOne { private SingletonOne() {} private static SingletonOne instance = null; public static SingletonOne getInstance() { // 此处存在线程安全 if (instance == null) { instance = new SingletonOne(); } return instance; } }
2. 饿汉模式
public class Singleton2 { private Singleton2() {} private static Singleton2 instance = new Singleton2(); // 类加载较慢 public static Singleton2 getInstance() { return Singleton2.instance; } }
3. 双重检查模式 (DCL)
public class Singleton3 { private Singleton3() {} private static Singleton3 instance = null; public static Singleton3 getInstance() { if (null == instance) { // JVM编译器的指令重排 导致 线程安全 synchronized (Singleton3.class) { if (instance== null) { instance= new Singleton3(); } } } return instance; } }
4. 双重检查模式 - 优化版
public class Singleton4 { private Singleton4() {} // volatile 关键字 禁止指令重排序 private volatile static Singleton4 instance = null; public static Singleton4 getInstance() { if (null == instance) { synchronized (Singleton4.class) { if (instance== null) { instance= new Singleton4(); } } } return instance; } }
5. 静态内部类
/** * 1.从外部无法访问静态内部类LazyHolder,只有当调用Singleton.getInstance方法的时候,才能得到单例对象INSTANCE。 * 2.INSTANCE对象初始化的时机并不是在单例类Singleton被加载的时候,而是在调用getInstance方法,使得静态内部类LazyHolder被加载的时候。 * 因此这种实现方式是利用classloader的加载机制来实现懒加载,并保证构建单例的线程安全。 */ public class Singleton5 { private Singleton5() {} private static class LazyHolder { private static final Singleton5 INSTANCE = new Singleton5(); } public static Singleton5 getInstance() { return LazyHolder.INSTANCE; } }
5.1 反射打破单例模式的约束
//获得构造器 Constructor con = Singleton5.class.getDeclaredConstructor(); //设置为可访问 con.setAccessible(true); //构造两个不同的对象 Singleton5 singleton1 = (Singleton5)con.newInstance(); Singleton5 singleton2 = (Singleton5)con.newInstance(); //验证是否是不同对象 System.out.println(singleton1); System.out.println(singleton2); // 结果 singleton.Singleton5@4554617c singleton.Singleton5@74a14482
6. 枚举
// 防止利用反射强行构建单例对象 public enum Singleton6 { INSTANCE; public void doSomething(){ } }
7. 总结
实现 | 线程安全 | 懒加载 | 防止反射构建 |
---|---|---|---|
懒汉模式 | 否 | 是 | 否 |
饿汉模式 | 是 | 否 | 否 |
双重检查模式 | 是 | 是 | 否 |
静态内部类 | 是 | 是 | 否 |
枚举 | 是 | 否 | 是 |
-
volatile关键字不但可以防止指令重排,也可以保证线程访问的变量值是主内存中的最新值。有关volatile的详细原理,我在以后的漫画中会专门讲解。
-
使用枚举实现的单例模式,不但可以防止利用反射强行构建单例对象,而且可以在枚举类对象被反序列化的时候,保证反序列的返回结果是同一对象。
-
对于其他方式实现的单例模式,如果既想要做到可序列化,又想要反序列化为同一对象,则必须实现readResolve