【设计模式-单例】
定义
单例设计模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问该实例。单例模式常用于需要控制资源的共享访问、实现全局配置、日志记录等场景。
特点
- 唯一性:类的实例是唯一的,且只有一个。
- 全局访问点:提供一个静态方法,允许外界获取这个唯一实例。
实现单例模式的步骤
- 私有化构造方法:防止外部通过构造方法直接创建对象。
- 创建私有静态实例:该实例是类的唯一实例。
- 提供公有静态方法:通过这个方法获取唯一实例。
实现方式
饿汉式
public class Singleton { private static Singleton instance = new Singleton(); private Singleton (){} public static Singleton getInstance() { return instance; }
}
懒汉式(线程不安全)
public class LazySingleton {private static LazySingleton instance;private LazySingleton() {}//懒汉式,线程不安全public static LazySingleton getInstance() {if (instance==null) {instance=new LazySingleton();}return instance;}
}
懒汉式(线程安全)
public class LazySingleton {private static LazySingleton instance;private LazySingleton() {}//懒汉式,线程安全,但效率低public static synchronized LazySingleton getInstanceSafe() {if (instance==null) {instance = new LazySingleton();}return instance;}
}
静态内部类
public class Singleton { // 静态内部类private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){} public static final Singleton getInstance() { return SingletonHolder.INSTANCE; }
}
静态内部类
- 延迟加载:静态内部类是在第一次被访问时加载,保证了延迟加载(Lazy Initialization)。这意味着如果程序运行期间从未调用 getInstance() 方法,SingletonHelper 类和 INSTANCE 实例都不会被加载和创建。
- 线程安全性:JVM 的类加载机制天然具有线程安全性,保证了类加载和静态变量初始化的过程不会被多线程打断。因此,即使在多线程环境下,静态内部类的单例模式也无需显式同步。
双重检查锁定
public class Singleton {private static volatile Singleton instance;private Singleton() {// 初始化代码}public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}
}
volatile
-
可见性:在多线程编程中,每个线程都有自己的工作内存,线程从主内存中读取变量的值到自己的工作内存中,进行操作后再写回主内存。这意味着,如果一个线程更新了一个变量的值,其他线程可能无法立即看到这个变化。
当一个变量被声明为 volatile,它确保了该变量的修改对所有线程立即可见。也就是说,当一个线程修改了 volatile 变量的值,这个新值会立即被写回主内存,而其他线程读取这个变量时,也会直接从主内存读取最新的值。 -
防止指令重排序:指令重排序是指编译器或处理器在优化代码时,为了提高性能,可能会改变指令的执行顺序。虽然单线程下这种优化通常不会影响程序的正确性,但在多线程环境中可能导致意想不到的错误。
当一个变量被声明为 volatile 时,它会禁止 JVM 对这个变量的读写操作进行指令重排序。即使是在多线程环境下,所有对这个变量的读写操作都会按照程序中出现的顺序执行,从而防止由于指令重排序而导致的并发问题。
枚举
public enum Singleton { INSTANCE;
}
种方式是Effective Java作者Josh Bloch提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒啊。所以这种写法,是十分推荐的且是最优的
总结
饿汉式适用于资源不敏感的单例,因为它实例化较早。
懒汉式和双重检查锁定适合需要延迟加载的场景,但要考虑线程安全问题。
静态内部类和枚举是推荐的实现方式,因为它们在保证线程安全的同时具备良好的性能和安全性。
参考
单例模式中枚举最好