懒汉模式 指令重排序问题
- 一. 懒汉模式的意义和代码实现
- 二. 饿汉模式和懒汉模式的线程安全
- 三. 懒汉模式的线程安全问题解决
- 3.1 加锁阶段
- 3.2 嵌套if阶段
- 3.3 指令重排序问题
- 3.4 解决线程安全问题阶段
一. 懒汉模式的意义和代码实现
在上一章节中,我们先学习了单例模式中的饿汉模式,所谓饿汉,就是在调用getinstance方法之前,一开始就创建出相应的实例.
在这一章节中,我们的懒汉模式就不一样了,懒汉模式是在什么时候使用实例,就什么时候创建出相应的实例.
如果是首次调用getInstance方法,那么就会创建出相应的实例,如果是后续第二次甚至多次调用getIstance方法就不会再创建实例,就会直接返回instance.
二. 饿汉模式和懒汉模式的线程安全
当我们在看懒汉模式的时候,就会发现相应的问题.
三. 懒汉模式的线程安全问题解决
那么我们如何改进懒汉模式的线程安全问题呢?当然是用我们之前学过的加锁.
3.1 加锁阶段
加锁归加锁,我们在哪里进行加锁呢?
我们先看第一个地方加锁.
我们来看正确的代码
那么问题又来了,加锁意味着与"高性能无缘",我们应该怎么调整代码来提高性能呢?
3.2 嵌套if阶段
我们在锁的外面再加一层if条件判断, 来判断这个线程是否需要加锁.
最外层if判断线程是否需要加锁,内层if判断instance是否为空或者是否要创建对象.
3.3 指令重排序问题
但是上述代码还有一些问题,就涉及到了线程不安全的因素之一的
指令重排序问题,意思就是在保证逻辑不变的前提下,调整代码执行的顺序,提高代码执行的效率.
在上述创建实例的场景下,我们分为三个步骤
- 申请一段内存
2.在内存上调用构造方法,创建实例
3.将内存地址赋值给instance
由于我们先执行的1和3,那么instance的值不为空,在执行t2线程时,就会直接返回instance对象,就会出现相应的问题.
3.4 解决线程安全问题阶段
这个时候,解决这个问题就需要用到前面在内存可见性提到的
volatile关键字,