第一种 饿汉式
public class A {
private static A a = new A();
private A() { }
public static A getInstance() {
return a;
}
}
缺点: 类加载的时候就会初始化,没有有懒加载的效果
第二种 懒汉式
public class A {
private static A a = null;
private A() { }
public static synchronized A getInstance() {
if (a == null) {
a = new A();
}
return a;
}
}
缺点: 每次调用方法都会去获取锁,影响性能
第三种 双从检查锁定和volatile (推荐)
public class A {
private volatile static A a = null; // 标注1
private A() { }
public static A getInstance() {
if (a == null) { // 标注3
synchronized (A.class) {
if (a == null) {
a = new A(); // 标注2
}
}
}
return a;
}
}
标注2 : new A() 这个操作会被翻译成三步执行,
- memory = allocate(); //1. 分配对象的内存空间
- ctorInstance(memory); //2. 初始化对象
- instance = memory; //3. 设置instance指向刚分配的内存地址
这三个操作中2,3 可能会被编译器或者处理器优化重新排序,先执行3,在执行2,那么如果同时有线程A 执行到了 标注3,线程B 执行到了标注2*,对于A 线程来说拿到的 a 引用不等于空,所以直接return
标注1: 为了解决上面的问题,加了volatile
;禁止了重新排序,具体参考volatile的重新排序规则
第四种: 基于类初始化
public class A {
private static class InstanceHolder {
private static A a = new A();
}
public static A getInstance() {
return InstanceHolder.a; // 这里将导致InstanceHolder类被初始化
}
}
JVM在类的初始化阶段(即在Class被加载后,且被线程使用之前),会执行类的初始化。在执行初始化期间,JVM会去获取一个锁。

网友评论