并发源头
一、cpu缓存导致的可见性问题
-+96
多核 CPU 的缓存与内存关系图
Java解决方案
通过内存模型的 Happens-Before 规则解决,即volatile关键字、sychronized锁等
二、线程切换带来的原子性问题
程序的一个操作语句在我们看来是一个不可分割的整体,但却可以拆分成多条cpu指令。
我们把一个或者多个操作在 CPU 执行的过程中不被中断的特性称为原子性
Java解决方案
原子性问题是因为有线程切换,通过互斥锁来保证同一时刻只有一个线程执行
三、编译优化带来的有序性问题
经典案例:利用双重检查创建单例对象
public class Singleton{
private Singleton instance;
public Singleton getSingleton(){
if(sinstance == null) {
synchorized(instance){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
创建对象:优化前
- 申请一块内存M
- 在M上初始化Singleton对象
- 然后将M的地址赋值给Singleton
创建对象:优化后
- 申请一块内存M
- 将M的地址赋值给Singleton
- 在M上初始化Singleton对象
所以在第一个if判断时,可能导致不通过,而返回一个未初始化的对象,导致空指针异常。
Java解决方案
通过内存模型的 Happens-Before 规则限制优化