单例模式这样写比较严谨

我先贴出正确的写法,在分析(注意标红部分):

单例模式这样写比较严谨

注意点已经在代码标红,下面分析巧妙之处:

(1) 避免锁定整个 getInstance() ,如果锁定整个获取实例的方法,那么多线程每次获取的时候,都有可能等待,等其他线程执行完,会有性能的损失。所以在先在(1)处判断一下,非空的话,直接拿出来用。

(2)第二个if(instance==null) 是因为,进入同步块的时候,可能其他线程已经创建完毕,所以再判断一下。

(3)volatile 的作用是多线程之间,保证变量instance的可见性,避免因为每个线程的工作内存里面的变量instance 的值不同而取脏数据。这里我简单说一下工作内存:每个线程有自己的工作内存的,如果工作内存没有它要用的对象,它会到主内存去读,它更新这个对象的时候,它会先写到工作内存,然后再写到主内存。由于每个线程都有自己的工作内存,所以可能会读脏数据。当对象加了volatile ,就会保证 该对象一旦改变,会写到主内存,并且通知其他线程到主内存去读。

instance = new Singleton();  这句在执行的时候,可以分解为3行伪代码如下:

1 memory=allocate();// 分配内存 相当于c的malloc

2 ctorInstanc(memory) //初始化对象

3 instance=memory //设置instance指向刚分配的地址


上面的代码在编译器运行时,可能会出现重排序 从1-2-3 排序为1-3-2

如此在多线程下就会出现问题

例如现在有2个线程A,B

线程A在执行new Singleton()时,B线程进来,而此时A执行了 1和3,没有执行2,此时B线程判断instance不为null (分配了空间即非空,但是对象还没初始化), 直接返回一个未初始化的对象,就会出现问题

而用了volatile,上面的重排序就会在多线程环境中禁止,不会出现上述问题。