四、单例模式

11 阅读1分钟

单例模式有两种,懒汉式和饿汉式,下面是两种实现方式

  • 饿汉式

饿汉式中,单例对象的创建是在类加载过程中完成的

public class HungrySingle {

    private HungrySingle() {}

    private static HungrySingle hungrySingle = new HungrySingle();

    public static HungrySingle getInstance() {
        return hungrySingle;
    }
}
  • 懒汉式

懒汉式中,单例对象的创建是在第一次获取对象时

第一种实现,在 getInstance 方法上加 synchronized 锁

public class LazySingle {

    private static LazySingle lazySingle;

    private LazySingle() {

    }

    public static synchronized LazySingle getInstance() {
        if (lazySingle == null) {
            lazySingle = new LazySingle();
        }
        return lazySingle;
    }
}

第二种实现,双重判空实现

public class LazySingle {

    private static volatile LazySingle lazySingle;

    private LazySingle() {

    }

    public static LazySingle getInstance() {
        if (lazySingle == null) {
            synchronized (LazySingle.class) {
                if (lazySingle == null) {
                    lazySingle = new LazySingle();
                }
            }
        }
        return lazySingle;
    }
}

需要注意,这种实现方式必须要在变量上加上 volatile 关键字。

因为 new 对象的操作不是原子性的,可以分为三个步骤:

  1. 分配内存空间
  2. 执行构造方法初始化对象
  3. 创建对象的引用,引用指向内存空间

由于创建对象的操作不是原子性的,可能会发生先执行步骤3,再执行步骤2的情况。如果在线程 A 执行步骤2之前,线程 B 执行了 if 判断语句,由于线程 A 执行了步骤3,所以线程 B 会认为对象不为 null,就会直接返回没有初始化的对象,可能会导致后续代码出问题。