为什么使用单例模式?

  • 处理资源访问冲突 (互斥访问资源)
  • 表示全局唯一类
    • 配置类
    • 唯一递增 ID 号码生成器

如何实现一个单例?

  • 构造函数需要是 private 访问权限的,这样才能避免外部通过 new 创建实例;
  • 考虑对象创建时的线程安全问题;
  • 考虑是否支持延迟加载;
  • 考虑 getInstance() 性能是否高(是否加锁)。

1. 饿汉式

在类加载的时候,instance 静态实例就已经创建并初始化好了,所以,instance 实例的创建过程是线程安全的。

2. 懒汉式

懒汉式相对于饿汉式的优势是支持延迟加载。

缺点:

3. 双重检测

既支持延迟加载、又支持高并发的单例实现方式.

4. 静态内部类

一种比双重检测更加简单的实现方法,那就是利用 Java 的静态内部类。它有点类似饿汉式,但又能做到了延迟加载。

  1. public class IdGenerator {
  2. private AtomicLong id = new AtomicLong(0);
  3. private IdGenerator() {}
  4. private static class SingletonHolder{
  5. private static final IdGenerator instance = new IdGenerator();
  6. }
  7. public static IdGenerator getInstance() {
  8. return SingletonHolder.instance;
  9. }
  10. public long getId() {
  11. return id.incrementAndGet();
  12. }
  13. }

SingletonHolder 是一个静态内部类,当外部类 IdGenerator 被加载的时候,并不会创建 SingletonHolder 实例对象。只有当调用 getInstance() 方法时,SingletonHolder 才会被加载,这个时候才会创建 instance。instance 的唯一性、创建过程的线程安全性,都由 JVM 来保证。所以,这种实现方法既保证了线程安全,又能做到延迟加载。

5. 枚举

这种实现方式通过 Java 枚举类型本身的特性,保证了实例创建的线程安全性和实例的唯一性。