单例模式
定义
单例模式,属于创建类型的一种常用的软件设计模式。通过单例模式的方法创建的类在当前进程中只有一个实例(根据需要,也有可能一个线程中属于单例,如:仅线程上下文内使用同一个实例)
说明:
这一模式的目的是使得类的一个对象成为系统中的唯一实例。要实现这一点,可以从客户端对其进行实例化开始。因此需要用一种只允许生成对象类的唯一实例的机制,“阻止”所有想要生成对象的访问。使用工厂方法来限制实例化过程。这个方法应该是静态方法(类方法)
要点:
- 某个类只能有一个实例
- 它必须自行创建这个实例
- 它必须自行向整个系统提供这个实例
使用场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session 工厂等)
单例模式八种实现方式
- 饿汉式(静态常量)
- 饿汉式(静态代码块(和第一个差别不大))
- 懒汉式(线程不安全)
- 懒汉式(线程安全,同步方法)
- 懒汉式(线程安全,同步代码块)
- 双重检查
- 静态内部类
- 枚举
1、饿汉式(静态常量)

1 class Singleton { 2 // 类内部创建对象 3 private static final Singleton instance; 4 static { 5 instance = new Singleton(); 6 } 7 // 构造器私有化(防止 new 生成对象) 8 private Singleton() { 9 } 10 11 // 对外暴露一个 static public 方法 (getInstance) 12 public static Singleton getInstance() { 13 return instance; 14 } 15 } 16 17 public static void main(String[] args) { 18 Singleton s1 = Singleton.getInstance(); 19 Singleton s2 = Singleton.getInstance(); 20 21 System.out.println("s1 == s2 ? " + (s1 == s2)); 22 System.out.println("s1.hashCode() = " + s1.hashCode()); 23 System.out.println("s2.hashCode() = " + s2.hashCode()); 24 }
基于 classloader 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。
优点:避免了多线程的同步问题,没有加锁,效率高;易实现
缺点:类加载时就初始化,没有 lazy loading,可能造成内存浪费
2、饿汉式(静态代码块)
3、懒汉式(线程不安全)
(多线程时,线程一 instance == null 准备 new 一个对象,而这时线程二也可以通过 if 的判断,可能会new 两个对象)

1 class Singleton { 2 private static Singleton instance; 3 4 private Singleton() { 5 } 6 7 // 使用时才创建 instance 8 public static Singleton getInstance() { 9 if (instance == null) { 10 instance = new Singleton(); 11 } 12 return instance; 13 } 14 }
这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。
优点:lazy loading、易实现
缺点:线程不安全
4、懒汉式(线程安全)

1 class Singleton { 2 private static Singleton instance; 3 4 private Singleton() { 5 } 6 7 // synchronized 同步代码,解决线程安全问题 8 public static synchronized Singleton getInstance() { 9 if (instance == null) { 10 instance = new Singleton(); 11 } 12 return instance; 13 } 14 }
这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。
优点:lazy loading
缺点:效率低
5、懒汉式(线程安全(其实不安全,效率还低))

1 class Singleton { 2 private static Singleton instance; 3 4 private Singleton() { 5 } 6 7 // 线程不安全 8 public static Singleton getInstance() { 9 if (instance == null) { 10 synchronized (Singleton.class) { 11 instance = new Singleton(); 12 } 13 } 14 return instance; 15 } 16 }
这种写法问题同方式3,不能用这种方式
6、双重检查
volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。
详见 https://baike.baidu.com/item/volatile/10606957?fr=aladdin#3 —— 百度百科

1 class Singleton { 2 private static volatile Singleton instance; 3 4 private Singleton() { 5 } 6 //双重检查,线程安全,lazy loading 7 public static synchronized Singleton getInstance() { 8 if (instance == null) { 9 synchronized (Singleton.class) { 10 if (instance == null) { 11 instance = new Singleton(); 12 } 13 } 14 } 15 return instance; 16 } 17 }
这种方式采用双锁机制,安全且在多线程情况下能保持高性能。只有第一次在 instance = null 时才会同步代码
Lazy loading、高效率 、实现较复杂
7、静态内部类

class Singleton { private Singleton() { } // 静态内部类提供实例 private static class SingletonInstance { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return SingletonInstance.INSTANCE; } }
这种方式利用了 classloader 机制来保证初始化 instance 时只有一个线程
利用静态内部类延迟加载的机制达到Lazy loading的效果,同时避免了线程安全问题,且效率高
8、枚举

1 enum Singleton { 2 INSTANCE; 3 public void fun() { 4 } 5 } 6 7 public static void main(String[] args) { 8 Singleton s1 = Singleton.INSTANCE; 9 Singleton s2 = Singleton.INSTANCE; 10 11 System.out.println("s1 == s2 ? " + (s1 == s2)); 12 System.out.println("s1.hashCode() = " + s1.hashCode()); 13 System.out.println("s2.hashCode() = " + s2.hashCode()); 14 }
这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。
源码:
JDK中 java.lang.Runtime 就是典型的单例模式的饿汉式
总结:
一般情况下,不建议使用懒汉式,建议使用饿汉式。只有在要明确实现 lazy loading 效果时,才会使用静态内部类。如果涉及到反序列化创建对象时,可以尝试使用枚举。如果有其他特殊的需求,可以考虑使用双重检查。
参考资料
https://baike.baidu.com/item/%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F/5946627?fr=aladdin#1 —— 百度百科
https://www.bilibili.com/video/BV1G4411c7N4? —— 尚硅谷,韩顺平
https://www.runoob.com/design-pattern/singleton-pattern.html —— 菜鸟教程