java中5种实现单例模式的方法

1. 为什么会需要单例:

1. 节省内存,单例对象可避免频繁的创建与销毁,带来性能的提升。
2. 对象的共享,例如配制。

2. 实现单例的方式

饿汉式
懒汉式
双重检测
静态内部类
枚举

2.1 单例-饿汉式

public class UniqueIdGenerator {

  private static final UniqueIdGenerator INSTANCE = new UniqueIdGenerator();

  /** 私有化构建函数 */
  private UniqueIdGenerator() {}

  public static UniqueIdGenerator getInstance() {
    return INSTANCE;
  }

  public String getUniqueId() {
    return UUID.randomUUID().toString();
  }
}

优势:

实现简单
由于在类加载期间已经就已经将实例初始化完成,创建实例对象无线程安全问题

劣势:

如果初始化非常耗时,将导致系统启动缓慢。
不支持延迟加载,由于在启动时,就直接加载了,无法做到按需加载。
极端情况下,还是可以拿到多实例,如通过反射。

2.2 懒汉式

public class UniqueIdGenerator {

  private static UniqueIdGenerator INSTANCE;

  /** 私有化构建函数 */
  private UniqueIdGenerator() {}

  public static synchronized UniqueIdGenerator getInstance() {
    if (INSTANCE == null) {
      INSTANCE = new UniqueIdGenerator();
    }

    return INSTANCE;
  }

  public String getUniqueId() {
    return UUID.randomUUID().toString();
  }
}

优势:

支持延迟加载,可做到按需加载。
线程安全

劣势:

性能瓶颈,这种实现方式,导致每次调用都需要获取锁与释放锁,在大量并发请求时将产生性能问题。
并发度低。由于加入了synchronized,并行度为1,导致并发度低。
极端情况下,还是可以拿到多实例,如通过反射。

2.3 双重检测

public class UniqueIdGenerator {

  private static UniqueIdGenerator INSTANCE;

  /** 私有化构建函数 */
  private UniqueIdGenerator() {}

  public static UniqueIdGenerator getInstance() {
    if (null == INSTANCE) {
      // 类级别的锁
      synchronized (UniqueIdGenerator.class) {
        if (null == INSTANCE) {
          INSTANCE = new UniqueIdGenerator();
        }
      }
    }
    return INSTANCE;
  }

  public String getUniqueId() {
    return UUID.randomUUID().toString();
  }
}

优势:

支持延迟加载,可做到按需加载。
又支持高并发

劣势:

这个劣势较小,由于使用了锁,使用时需要小心。
极端情况下,还是可以拿到多实例,如通过反射。

2.4 静态内部类

public class UniqueIdGenerator {

  private static UniqueIdGenerator INSTANCE;

  /** 私有化构建函数 */
  private UniqueIdGenerator() {}

  /** 通过静态内部类获取实例 */
  private static class SingleInner {
    private static final UniqueIdGenerator INSTANCE = new UniqueIdGenerator();
  }

  public static UniqueIdGenerator getInstance() {
    return SingleInner.INSTANCE;
  }

  public String getUniqueId() {
    return UUID.randomUUID().toString();
  }
}

优势:

支持延迟加载,可做到按需加载。
又支持高并发
相比双重检查,实现更为简单,无需再使用锁。

劣势:

极端情况下,还是可以拿到多实例,如通过反射。

2.5 枚举

public enum UniqueIdGenerator {
  INSTANCE;

  public String getUniqueId() {
    return UUID.randomUUID().toString();
  }
}

优势:

支持延迟加载,可做到按需加载。
又支持高并发
最极端的情况,也无法拿到多实例。
java最推荐的写法。