懒汉式
加方法锁
public class Singleton {
private static Singleton singleton = null;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (Objects.isNull(singleton)) {
singleton = new Singleton();
}
return singleton;
}
}
- 直接在 getInstance() 方法加锁,但是加锁的范围太大,性能低下
双重检查锁定
public class Singleton {
private static volatile Singleton singleton = null;
private Singleton() {
}
public static Singleton getInstance() {
// 1
if (Objects.isNull(singleton)) {
synchronized(Singleton.class) {
if (Objects.isNull(singleton)) {
// 2
singleton = new Singleton();
}
}
}
return singleton;
}
}
对象需要加volatile 关键字,主要是防止指令重排序。
singleton = new Singleton();
方法在执行的时候有三个指令:memory = allocate(); // 1:分配对象的内存空间 ctorInstance(memory); // 2:初始化对象 instance = memory; // 3:设置instance指向刚分配的内存地址
当线程A获取到锁,执行初始化的时候发生了指令重排,1->3->2。当2还没有被执行时,线程B执行到代码标记1的位置,这时判断到对象不为空,直接返回该对象,但是这个时候该对象可能还并没有完成初始化,导致线程B在执行过程中抛错。
静态内部类
public class Singleton {
private static class LazyHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton() {
}
public static Singleton getInstance() {
return LazyHolder.INSTANCE;
}
}
既实现了线程安全,又避免了同步带来的性能影响。JVM在类的初始化阶段(即在Class被加载后,且被线程使用之前),会执行类的初始化。在 执行类的初始化期间,JVM会去获取一个锁。这个锁可以同步多个线程对同一个类的初始化。使用这种方式,我们是允许
new Singleton();
过程发生指令重排的。
使用枚举的形式
public class EnumSingleton {
private EnumSingleton() {
}
public static EnumSingleton getInstance() {
return Singleton.INSTANCE.getInstance();
}
private enum Singleton {
INSTANCE(new EnumSingleton());
private EnumSingleton singleton;
//JVM会保证此方法绝对只调用一次
Singleton(EnumSingleton singleton) {
this.singleton = singleton;
}
public EnumSingleton getInstance() {
return singleton;
}
}
}
JVM会保证枚举类构造方法绝对只调用一次,所以保证了对象实例的唯一性
饿汉式
public class Singleton {
private static final Singleton singleton = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return singleton;
}
}