1. 饿汉式单例(Eager Initialization)
原理:在类加载时就创建实例。这种方式是线程安全的,因为JVM在类加载时会自动确保线程安全。
优点:实现简单,线程安全。
缺点:即使不使用这个实例,它也会被创建,可能会浪费内存。
2. 懒汉式单例(Lazy Initialization)
原理:在第一次需要使用实例时才创建实例。这种方式在单线程环境下是安全的,但在多线程环境下需要额外的同步控制。
线程安全的实现方法:
2.1 使用 synchronized
关键字
原理:在获取实例的方法上加锁,确保只有一个线程能够创建实例。
优点:简单易懂,确保线程安全。
缺点:加锁会影响性能,特别是在高并发情况下。
2.2 双重检查锁(Double-Checked Locking)
原理:减少加锁的开销,通过两次检查实例是否已创建,只有在实例未创建时才进行加锁。
优点:性能较好,只有在第一次创建实例时才会加锁。
缺点:实现稍微复杂,需要确保内存可见性问题。
3. 静态内部类(Static Inner Class)
原理:利用类加载机制创建单例实例。静态内部类只有在第一次被使用时才会被加载,这样既实现了懒加载,又确保了线程安全。
优点:实现简单,线程安全,支持延迟加载。
缺点:无明显缺点,推荐使用。
4. 枚举单例(Enum Singleton)
原理:利用Java枚举类型的特性来实现单例。枚举类型本身是线程安全的,并且只会被实例化一次。
优点:实现简单,线程安全,防止反序列化创建新的对象。
缺点:某些情况下不够灵活。
总结
- 饿汉式单例:简单直接,但可能浪费内存。
- 懒汉式单例:需要额外的同步控制,双重检查锁是较好的实现方式。
- 静态内部类:推荐使用,既实现了懒加载,又确保了线程安全。
- 枚举单例:最简单且最安全的实现方式,但灵活性稍差。
推荐实现方式
静态内部类实现(推荐):
这种方式结合了饿汉式和懒汉式的优点,既实现了延迟加载,又确保了线程安全,同时避免了加锁带来的性能开销。