今天在牛客上看到一个面经,这一个人在面试之后的总结,我在里面看一个题目非常有意思,今天自己记录一下。
首先我感觉这个面试官水平非常高,他面试,是循序渐进的,例如:他今天面试的这个,就是一步步扩展知识面一步步引导面试生,由项目引出里氏替换原则(是一种软件设计原则),然后继续引出什么是单例模式?接着要他手写一个单例模式
//手写单例模式(懒汉式)
private static Singleton getSingleton(){
//这里的 new Singleton 真的不会实例多次吗?(多线程情况下)
if (singleton==null){
singleton = new Singleton();
}
return singleton;
}
就上面代码中说出的这个问题,我们最先想到的是synchronized 上锁
private synchronized static Singleton getSingleton(){
//这里的 new Singleton 真的不会实例多次吗?(多线程情况下)
if (singleton==null){
singleton = new Singleton();
}
return singleton;
}
这里的synchronized锁的是对象还是方法或者是代码块?
不管这synchronized放在这个方法上还是放在这个方法内部,都是锁的对象。
锁的是对象,又因为这是个静态方法 ,所以调用者自然是这个类。
所以synchronized这样上锁肯定会造成性能问题,在变通一下,我们把这个synchronized放在真正产生冲突处.
private static Singleton getSingleton(){
if (singleton==null){
synchronized (Singleton.class{
singleton = new Singleton();
}
}
return singleton;
}
又产生一个问题,多线程情况下,两个线程A线程B线程,A线程发现这个对象没有new,他直接new出来,B线程,同事也发现这个对象没有new,又new了一遍。
解决方法:
private static Singleton getSingleton(){
if (singleton==null){
synchronized (Singleton.class){
if (singleton==null){
singleton = new Singleton();
}
}
}
return singleton;
}
这个时候为了保证线程安全问题,双重检测模型,实现单例模式。
![](https://img-blog.csdnimg.cn/img_convert/9df438e894774fd69d7cbc69c13ee0f5.png)
这个时候volatile出现了,它的出现能解决什么问题?
volatile修饰的属性,它是属性修饰符,你方法上不行。大家都知道这个属性,是一个成员变量,那既然是一个变量,是一块内存空间,那么说给这个,内存空间加上volatile有两个特点。
它的作用是可以解决一个可见性的问题
防止指令重排序问题
下面这两个问题,我来画图说一下
第一个问题
![](https://img-blog.csdnimg.cn/img_convert/337cc194712643fab0e7f0bcb601a394.png)
下面三个方块你可以认为是三个线程,第一个线程 在拿到属性a然后将属性变为1,然后其他两个线程,看到却还是a=0
![](https://img-blog.csdnimg.cn/img_convert/601d8058af5748d2aae9d6b4cec4bf99.png)
加上volatile 后相当于告诉其他两个线程这个属性的只要变成a=1,这就是解决了属性的可见性问题。
第二个问题是,也就是我们刚才的问题,单例的时候对象创建的问题,创建我们给了一行指令
new Singleton();
1、发一个new 指令,申请一个空间
2、加载空间内部信息
3、执行构造方法
4、告知对象构建成功(返回地址引用给你)
启动因为2,3 步骤比较慢,CPU会做一个性能优化,就是这两个步骤可以,来回交换顺序的。
也就是说我现在要买一个房子,A同事说我这有好屋推荐,B同事也说,有好房子推荐,但是呢A同事是等房子装修好,并且内部摆设都弄好了,才准备把钥匙给我,B同事,说我先把钥匙给你,我再装修内部,这时候A同事也要给我钥匙,是不是造成了指令2重排了。
volatile就解决了这个种指令重排序问题。