以上的实现特点是:
- 懒惰实例化
- 首次使用getInstance(才使用synchronized加锁,后续使用时无需加锁
- 有隐含的,但很关键的一-点:第一个if使用了INSTANCE变量,是在同步块之外
在某个便俩个全部交给synchronized托管时,可以保证原子性,有序性和可见性。但是上述代码中INSTANCE变量在同步块外面还有,所以不能保证它的有序性,即在代码 ‘INSTANCE=new Singleton()’ 这条指令中可能会发生指令重排序,即先复制在初始化。这样另外一个线程在同步块外面判断INSTANCE是否为空时,条件不成立,返回INSTANCE,但是这个实例还未初始化(得到一个未初始化的实例)
double-checked locking解决为了解决指令重排序,防止线程获得一个未初始化的实例,在定义INSTANCE时加上volatile.
为什么这样就可以了呢?
读之前加屏障(不会讲读屏障之后的代码排在读屏障之后),写之后加屏障(不会将写之前的代码排在写屏障之后)。即实例化的字节码不会重排序到复制语句的下面,就保证了其他线程拿到的INSTANCE一定是实例化好的。
==happens-before规定了对共享变量的写操作对其它线程的读操作可见,==它是可见性与有序性的一套规则总结,抛开以下happens-before规则,JMM并不能保证一个线程对共享变量的写,对于其它线程对该共享变量的读可见
t1加锁后对变量进行写操作,不释放锁,t2线程就得不到锁,无法进行读,即保证了写操作对其他线程的读操作可见。
将写入的变量存在主存中,线程对变量的读操作区主存中去读
线程都执行结束了,所以对变量的操作对其他线程也是可见的
在线程被打断之前对变量的操作对其他线程都是可见的
在线程中将呗volat修饰的x=20以及以上的代码都写入到主存,且不允许重排序,即x,y对t2线程都可见



