《阿里巴巴Java开发手册》中有这么一个推荐:
这种单例模式是我经常写的,看到竟然是反例,网上查了查,还是有根据的。
这样的问题在于初始化代码:
instance = new Singleton();
JVM会将这段代码分成三步去执行:
a.分配内存空间;
b.构造Singleton;
c.将instance指向构造的实例。
如果执行的过程是a->b->c的话,那上面的代码是没有问题的,但是有时JVM会基于***指令优化的目的将指令重排***,导致指令执行流程变为a->c->b。这样当线程A执行到4开始初始化单例对象的c流程时,线程B执行到1处,由于instance对象已经将内部指针指向分配的内存空间(即不为null),会直接返回未完全构造好的实例,从而出错。按照《手册》的说法,修改后的代码如下
由于volatile自带的“禁止指令重优化”语义,初始化语句只能按照a->b->c的顺序进行执行。
注:尽管这个问题看起来很简单,但是我在本地没有办法重演这个bug,这个bug出现的关键时刻在于线程A在执行a->c->b链的c时,线程B将构造完的instance返回并使用才会出错,但是一般的场景下是没有办法在这么短的时间间隔内捕获到这个间隔的。不过出于保险的目的,单例模式的我还是加上volatile修饰符比较好。