双检测+volatile
private volatile static Sington instance;
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
双检测:线程同步;
同步代码块:性能;
volatile:避免指令重排;
对于s = new Singleton();可以分解为3个步骤:
1 memory=allocate();// 分配内存 相当于c的malloc;
2 ctorInstanc(memory) //初始化对象;
3 s=memory //设置s指向刚分配的地址;
上面的代码在编译器运行时,可能会出现重排序 从1-2-3 排序为1-3-2,在多线程下就会出现问题,例如现在有2个线程A,B;线程A在执行第5行代码时,B线程进来,而此时A执行了 1和3,没有执行2,此时B线程判断s不为null 直接返回一个未初始化的对象,就会出现问题。有人说synchronized不是可以禁止重排序吗?这只是假象,是因为不加锁后是单线程,这种情况下无论重排序或者不重排序,结果都是一样的,synchronized并不是禁止了重排序,而是即使重排序,也不影响其结果!
对变量加上关键字volatile后,指令不会重排,就不会出现这样的问题。
静态内部类:
private SingleTon(){
}
private static SingleTonHolder{
public static SingleTon instance = new SingleTon();
}
public static SingleTon getInstance() {
return SingleTonHolder.instance;
}
1、静态变量在类的加载过程中就会进行初始化,静态代码块也是在类的加载过程中就会执行。
2、静态内部类和静态方法一样,静态方法只有被调用的时候才会执行,而静态内部类是只有在第一次调用的时候才会进行初始化。
顺便提一波类加载过程:
1、静态变量的初始化是在类加载过程中的准备阶段,但是这里仅仅是开辟内存空间,和赋初始值(比如 static int i = 100,这里会给i赋初始值0),真正在赋值是在类加载过程中的初始化阶段。
2、初始化阶段会进行静态变量的赋值,和静态代码块的执行操作。
初始化阶段是可选的,当使用Class.forName(String name, boolean initialize, ClassLoader loader)这个方法的时候,第二个参数为false的时候,就不会进行初始化,这个时候也不会有对静态变量的赋值,以及静态代码块的执行操作。