并行设计模式(1):单例模式

6 阅读2分钟

简单的单例实现

public class Singleton {
    public static int STATUS=1;
    private Singleton() {
        System.out.println("Singleton is create");
    }
    private static Singleton instance = new Singleton();
    public static Singleton getInstance() {
        return instance;
    }
}

这是一个典型的饿汉式的单例模式。

优点:

  1. 线程安全:在类加载阶段就完成了实例化,避免了多线程同步。
  2. 获取速度快:实例已经加载完成,可以直接返回
  3. 防止反射攻击

缺点:

  1. 内存浪费:无论是否使用,它都会创建
  2. 启动延迟:如果单例的初始化很耗时,会延迟应用的启动时间

懒加载的单例

public class LazySingleton {
    private LazySingleton() {
        System.out.println("LazySingleton is create");
    }
    private static LazySingleton instance = null;
    public static synchronized LazySingleton getInstance() {
        if (instance == null)
            instance = new LazySingleton();
        return instance;
    }
}

懒加载的思想是:最初并不需要实例化instance,⽽是当getInstance()⽅法被第⼀次调⽤时,创建单例对象。但这会引出线程同步的问题,因此需要使用synchronized来锁住同步。

双重检查的懒汉式

public class Singleton {
    // 关键:使用 volatile 修饰
    private static volatile Singleton instance;
    private Singleton() {}
    
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton(); // 现在安全了
                }
            }
        }
        return instance;
    }
}

有人评价它十分的丑陋和复杂,因此不做过多叙述。

静态内部类单例

public class Singleton {
    private Singleton() {}
    
    private static class Holder {
        private static final Singleton INSTANCE = new Singleton();
    }
    
    public static Singleton getInstance() {
        return Holder.INSTANCE; // 这里才会触发类加载,实现懒加载
    }
}

静态内部类是一个独立的类,它与外部类没有绑定关系。静态内部类和其外部类在编译后会生成两个独立的字节码文件.class文件)这意味着:

  1. 不会随外部类加载而加载:加载Singleton类时,不会自动加载Holder
  2. 有自己的类加载时机Holder类只在被引用时才加载

因此上述的单例模式,只有在调用Holder.INSTANCE时才会创建实例,其线程安全有JVM进行保证:

当多个线程同时首次调用getInstance()时:

  1. 第一个线程触发Holder类的加载
  2. JVM类加载机制保证<clinit>(类初始化方法)只会被执行一次
  3. 其他线程会等待类加载完成
  4. 所有线程都得到同一个已初始化的实例