单例模式有两种,懒汉式和饿汉式,下面是两种实现方式
- 饿汉式
饿汉式中,单例对象的创建是在类加载过程中完成的
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 对象的操作不是原子性的,可以分为三个步骤:
- 分配内存空间
- 执行构造方法初始化对象
- 创建对象的引用,引用指向内存空间
由于创建对象的操作不是原子性的,可能会发生先执行步骤3,再执行步骤2的情况。如果在线程 A 执行步骤2之前,线程 B 执行了 if 判断语句,由于线程 A 执行了步骤3,所以线程 B 会认为对象不为 null,就会直接返回没有初始化的对象,可能会导致后续代码出问题。