public class Singleton { /** * 单例对象实例 */ private volatile static Singleton instance = null; public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
这是一个典型的DCL单例,其中volatile在以前已经说过了,能够保证不管什么时候读取这个变量,都是读到内存中最新的值,不管什么时候写这个变量,均可以当即写到内存中。多线程
可是并无这么简单,在没有见volatile修饰instance时,在编译后,编译器会自动把第二个判断删除,由于编译器判断这个程序在执行过程当中,这个值是不会改变的,编译器不考虑多线程的状况。加了volatile,是告诉编译器,这个变量随时有可能会被其余线程改变,这样编译器就不会把这两个判断优化成一个判断了。优化
同时,volatile的变量,能够保证对该变量的操做具备原子性,典型的例子是long和double型变量,一般须要分两步读写一个double变量,volatile修饰的double能够保证对一个double变量的操做的两部分不会被多线程插入。以及对引用类型赋值的,new一个实例的过程不会被其余线程插入(new在编译指令中是分红几步执行的,防止这几步在执行过程当中被其余线程取这个变量值,取到一个不完整的实例)。能够简单的理解为对volatile变量的set和get方法加上了synchronized关键字,在new的过程当中,整个都处在set中,因此不会被其余线程的get打断,取到不完整的引用。spa
原子性针对一个long或者double或者一个引用类型,对于引用类型,原子性是指在new实例的过程当中,不会被其余线程取到,即不会被其余线程取到一个不完整的实例。这种原子性能够理解为new的过程处于一个synchronize段的set方法中,只有set结束才能够被get到,即new的整个过程都是处于set中的。也能够理解为指令重排序,禁止把new过程的指令与把引用赋值给变量的语句重排序,赋值只发生在new结束以后。线程
再次,在第二次判断if (instance == null)的时候,会再次取一次instance,再次取这个instance已是全部线程最新的,每次修改的引用都会实时反映到主内存中。code