Java设计模式【单例模式】
单例模式
优缺点
优点
:
由于在系统内存中只存在一个对象,因此可以节约系统资源。
缺点:
单例类的职责过重,在一定程度上违背了“单一职责原则”。
单例模式的使用
饿汉模式
静态成员变量
/** * @author Physicx * @date 2023/5/12 下午10:13 * @desc 单例 * Created with IntelliJ IDEA */ public class Singleton { //初始化实例对象 private static final Singleton instance = new Singleton(; //私有化构造方法 private Singleton( { } //提供获取实例对象方法 public static Singleton getInstance( { return instance; } }
- 静态代码块
/** * @author Physicx * @date 2023/5/12 下午10:13 * @desc 单例 * Created with IntelliJ IDEA */ public class Singleton { //实例对象 private static final Singleton instance; static { instance = new Singleton(; } //私有化构造方法 private Singleton( { } //提供获取实例对象方法 public static Singleton getInstance( { return instance; } }
饿汉式单例的写法适用于单例对象较少的情况,这样写可以保证绝对的线程安全,执行效率比较高。但是缺点也很明显,饿汉式会在类加载的时候就将所有单例对象实例化,这样系统中如果有大量的饿汉式单例对象的存在,系统初始化的时候会造成大量的内存浪费,换句话说就是不管对象用不用,对象都已存在,占用内存。
懒汉模式
public class Singleton { //实例对象 private static Singleton instance; //私有化构造方法 private Singleton( { } //提供获取实例对象方法(线程安全 public static synchronized Singleton getInstance( { if (instance == null { instance = new Singleton(; } return instance; } }
线程安全的一种懒汉式写法,在类第一次使用的时候初始化,获取实例的静态方法由synchronized修饰,所以是线程安全的。这种方法每次获取实例对象都加锁同步,效率较低。
双重检测机制(DCL)
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
修饰,否则极端情况可能出现安全隐患。
分配对象的内存空间。
设置instance指向刚才分配的内存空间。
分配对象的内存空间。
初始化对象。
DCL这种方式同样也是类第一次使用的时候初始化,初始化代码synchronized修饰线程安全,这种方式只会第一次实例对象才会进行同步,因此效率高。
静态内部类(延迟初始化)
public class Singleton { //私有化构造方法 private Singleton({} //静态内部类(被调用时加载) private static class SingletonHandle { private static final Singleton instance = new Singleton(; } //提供获取实例对象方法 public static Singleton getInstance( { return SingletonHandle.instance; } }
利用静态内部类被调用时才加载的特性,通过静态初始化初始Singleton对象,由于JVM将在初始化期间获得一个锁,并且每个线程都至少获取一次这个锁以确保这个类已经加载,因此在静态初始化期间,内存写入操作将自动对所有线程可见。因此无论是在被构造期间还是被引用时,静态初始化的对象都不需要显式的同步。
《Java Concurrency in Practice》作者Brian Goetz 推荐这种单例实现方式。
枚举实现方式
Google 首席 Java 架构师、《Effective Java》一书作者、Java集合框架的开创者Joshua Bloch在Effective Java一书中提到:单元素的枚举类型已经成为实现Singleton的最佳方法。
在这种实现方式中,既可以避免多线程同步问题;还可以防止通过反射和反序列化来重新创建新的对象。
public class Singleton { //私有化构造方法 private Singleton( {} enum SingletonEnum { SINGLETON; private final Singleton instance; SingletonEnum( { instance = new Singleton(; } //提供获取实例对象方法 public Singleton getInstance( { return instance; } } }
调用方式如下:
public static void main(String[] args { Singleton instance1 = Singleton.SingletonEnum.SINGLETON.getInstance(; Singleton instance2 = Singleton.SingletonEnum.SINGLETON.getInstance(; System.out.println(instance2 == instance1; }
普通的单例模式是可以通过反射和序列化/反序列化来破解的,jvm虚拟机会保证枚举类型不能被反射并且构造函数只被执行一次,而Enum由于自身的特性问题,是无法破解的。当然,由于这种情况基本不会出现,因此我们在使用单例模式的时候也比较少考虑这个问题。
总结
实现方式 | 优点 | 缺点 |
---|---|---|
饿汉模式 | 线程安全,效率高 | 非懒加载 |
懒汉模式 | 线程安全,懒加载 | 效率低 |
双重检测机制 | 线程安全,懒加载,效率高 | |
静态内部类 | 线程安全,懒加载,效率高 | |
枚举 | 线程安全,效率高 | 非懒加载 |
在很多书和文章中都强烈推荐将该方法作为单例模式的最佳实现方法。
设计模式相关其他文章:
Java设计模式总结