首先,我们从一个延迟初始化对象入手,来解析深度学习Synchronized。
public class UnsafeLazyInitizalization{
public static User user
public static User doubleCheckLock(){
if(user == null){
return new User();
}
return user;
}
}
首先我们思考一下这段代码,如果在并发的情况下,会出现怎么样的一个问题?
这段代码的目的是创建有且仅有一个的User对象,但在并发中会有这样的问题:
- A线程和B线程齐头并进,首先A线程进入了if判断语句中。
- A准备执行return new User()语句,B线程此时也执行了if判断语句。
- 这个时候A线程返回了新的User对象,而B线程也在随后也能够执行new User()语句。
- 此时A、B线程所获取的User对象是不同的两个对象
这时候,大家也应该跟我一样,给他加锁,解决并发问题!
好,这个时候我们将锁加上:
public class SafeLazyInitizalization{
public static User user
public synchronized static User doubleCheckLock(){
if(user == null){
return new User();
}
return user;
}
}
这样,看起来好像就万事大吉了,那我们能试着优化一下这段代码吗?
首先,这个锁加的,能够实现我们的线程可以有序的去执行对应的代码块,那是因为synchronized保证了数据的可见性,这是我们今天讲到synchronized的第一个知识点:可见性,也就是每个线程能够获取的数据都是最新的。
其次,我们来优化一下这段代码:
public class DoubleCheckLock{
public static User user
public static User doubleCheckLock(){
if(user == null){
synchronized(DoubleCheckLock.class){
return new User();
}
}
return user;
}
}
这段代码实现的功能跟上面的一致,但减少了性能的开销:将锁设置在了方法内,而不是在方法上;这样的实现也是早期的双重检查锁,在提高性能的同时又做到了线程安全。
但,事情真的这么简单吗?这样真的就万无一失了嘛?
我们先来了解一下一个对象的创建流程:
ins = new Instance()
memory = allocate(); // 1:分配对象的内存空间
ctorInstance(memory); //2:初始化对象
ins = memory; //3:设置ins指向分配的内存地址
这是创建一个对象所对应的指令,而synchronized无法保证这些指令不会重排序(这是源代码到指令序列所导致的一个问题) https://www.yuque.com/u22628569/uk26g9/pyikwdplrmeaxi0a?singleDoc#
也就可能出现下面的这种情况
| 时间 | 线程A | 线程B |
|---|---|---|
| t1 | A1:分配对象的内存空间 | |
| t2 | A3:设置ins指向内存空间 | |
| t3 | B1:判断ins是否为空 | |
| t4 | B2:由于ins不为null,线程B将访问ins引用的对象 | |
| t5 | A2:初始化对象 | |
| t6 | A4:访问ins引用的对象 |
也就是 2和3步骤被重排序了,这样就导致了B线程拿到的一个没被初始化的对象。这也就衍生出了Synchronized无法禁止指令重排序,那我们可以使用一个可以禁止指令重排序的修饰符volatile来避免这样的问题。
public class DoubleCheckLock{
public static volatile User user
public static User doubleCheckLock(){
if(user == null){
synchronized(DoubleCheckLock.class){
return new User();
}
}
return user;
}
}
这样,就是双重检查锁最终形态和为什么一个Synchronized解决不了并发情况下创建一个对象的原因。同时,我们也了解到Synchronized的两个特点:可见性、无法禁止重排序、(还有一个原子性)
当然,还有另外一种类初始化的解决方案,利用了类初始化后就会放到方法区的特点和类变量来实现的,有兴趣的可以点一下上面的链接去查看我的笔记
下一篇我们讲Synchronized的实现原理,以及锁升级