单例模式的实现
![dc780de3ae9d2da992b09c7f35e1d2f5.png](https://i-blog.csdnimg.cn/blog_migrate/2359db2a48424bc462faae211d168784.jpeg)
上面代码是一个经典的单例的双重监测的代码,这段代码在单线程环境下并没有什么问题,
但如果在是多线程环境下就可能出现线程安全问题。
多线程不安全的原因
上面代码不安全的原因如下:
当某一个线程执行到第一次监测,读取到的instance不为null时,
instance 的引用对象可能没有完全初始化。
因为 instance =new Singleton(); 可以分为以下三个步骤:
(1) memory=allocate();//1.分配对象内存空间
(2) instance(memory);//2.初始化对象
(3) instance=memory;//3.设置instance指向刚分配的内存地址,此时instance!=null
上面的 2 和 3有可能被重排序:如下
(1) memory=allocate();//1.分配对象内存空间
(2) instance=memory;//3.设置instance指向刚分配的内存地址,此时instance!=null,//但是对象还没有初始化完成!
i(3) nstance(memory);//2.初始化对象
多线程执行结果如下:
![340fcdbd07a6663d0bc1430edddb4005.png](https://i-blog.csdnimg.cn/blog_migrate/f0ca70a4b444297198e2aea2e6731c49.jpeg)
由于步骤2和步骤3 不存在数据依赖 关系,而且无论重排序前还是重排序后的执行结果在单线程环境下
是没有改变的,因此这种优化是允许的。但是指令重排只会保证串行语义的执行的一致性(单线程),但并
不关心多线程间的语义一致性。
所以当一条线程访问instance 不为null时,由于 instance 实例未必已初始化完成,也就造成了线程安全
问题。
怎样解决多线程下存在的问题
我们使用 volatile 禁止 instance变量被执行指令重排优化即可:
![f942215ca8aea349d7b4155fd104c1fe.png](https://i-blog.csdnimg.cn/blog_migrate/9aa155051aebfa14343058896d8630db.jpeg)