ThinPanda

在Java中如何写出一个正确的单例模式
单例模式是指保证一个类在全局只有一个实例,并且提供一个全局可以访问的入口为什么需要单例模式节省内存、节省计算很多情...
扫描右侧二维码阅读全文
03
2019/11

在Java中如何写出一个正确的单例模式

单例模式是指保证一个类在全局只有一个实例,并且提供一个全局可以访问的入口

为什么需要单例模式

  • 节省内存、节省计算

很多情况下,我们只需要一个实例就够了,如果出现了很多实例,反而属于浪费

  • 为了保证结果的正确
  • 方便管理,很多工具类只需要一个实例

单例模式的应用场景

  • 无状态的工具类

    • 日志工具
    • 字符串工具
  • 全局信息类

    • 全局计数
    • 环境变量

单例模式常见的写法

单例模式看似简单,但其实同时反映了并发、类加载、序列化等重要知识点的掌握程度和水平

单例模式有很多种写法:

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

优点:在类装载的同时就完成了实例化,避免了线程同步的问题

缺点:在类装载的时候完成实例化,没有达到懒加载的效果

public class Singleton {
    private static Singleton singleton = new Singleton();
    private Singleton(){}
    public static Singleton getInstance(){
        return singleton;
    }
}
静态代码块形式
public class Singleton {
  private static Singleton singleton;
  static {
    singleton = new Singleton();
  }
  private Singleton(){}
  public Singleton getInstance(){
    return singleton;
  }
}
懒汉式

只能在单线程下使用

public class Singleton {
  private static Singleton singleton;
  private Singleton(){}
  public static Singleton getInstance(){
    if(singleton == null){
      singleton = new Singleton();
    }
    return singleton;
  }
}
线程安全的懒汉式

线程安全,但是效率太低

public class Singleton {
  private static Singleton singleton;
  private Singleton(){}
  public static synchronized Singleton getInstance(){
    if(singleton == null){
      singleton = new Singleton();
    }
    return singleton;
  }
}

这种写法有问题,会发生线程安全问题

public class Singleton {
  private static Singleton singleton;
  private Singleton(){}
  public static Singleton getInstance(){
    if(singleton == null){
      synchronized (Singleton.class) {
        singleton = new Singleton();
      }
    }
    return singleton;
  }
}
线程安全的双重检查模式

优点:线程安全、延迟加载

public class Singleton {
  // 加 volatile 关键字原因:
  // 新建实例不是原子操作,防止发生指令重排序,拿到未完成初始化的对象
  private static volatile Singleton singleton;
  private Singleton(){}
  public static Singleton getInstance(){
    // 去掉它,所有线程都变成串行执行
    if (singleton == null) {
      synchronized (Singleton.class) {
        // 去掉它,会发生线程安全问题
        if (singleton == null) {
          singleton = new Singleton();
        }
      }
    }
    return singleton;
  }
}
静态内部类形式
public class Singleton {
  private Singleton() {}
  private static class SingletonInstance {
    private static final Singleton singleton = new Singleton();
  }
  public static Singleton getInstance() {
    return SingletonInstance.singleton;
  }
}

双重锁和静态内部类写法都能保证线程安全并且延迟加载,但是不能防止被反序列化生成多个实例

枚举写法
public class Singleton {
  private Singleton() {}
  public static Singleton getInstance() {
    return SingletonEnum.INSTANCE.getInstance();
  }
  // 枚举是线程安全的,并且JVM保证只会装载一次
  private enum SingletonEnum {
    INSTANCE;
    private final Singleton instance;
    SingletonEnum() {
      instance = new Singleton();
    }
    private Singleton getInstance() {
      return instance;
    }
  }
}
Last modification:September 3rd, 2020 at 10:59 am
如果觉得我的文章对你有用,请随意赞赏

Leave a Comment