===volatile作用?volatile的实现原理是什么呢?lock前缀的效果是什么呢?
volatile的作用:
1.可见性。一个线程对volatile变量进行写操作之后,其他线程都能够看到。
2.原子性。单个volatile变量的读写具有原子性,但是复合操作不满足原子性。
用来修饰变量
对volatile修饰的变量进行修改操作的时候,会在操作指令之前添加上LOCK前缀。
LOCK前缀的效果是:命名
1.当前处理器缓存行的数据会写回到系统内存的时候会先获取总线,同时其他cpu阻塞。
2.保证缓存一致性,会使得其他CPU缓存的数据失效。因为其他cpu会不断嗅探总线,一发现自己缓存的数据被修改了就做失效标记。被标记之后要用的时候会重新从系统内存拿。
缓存一致性可能存在的问题:大量缓存一致性流量
volatile本身的内存语义
线程写volatile变量相当于给接下来要读取这个变量的线程发送消息。
线程读volatile变量相当于接收之前某个线程发出的消息。
两种都要经过主内存这个中间桥梁。
volatile读写防止指令重排序
能够建立一个内存屏障,一定程度上禁止指令重排序。重排序的时候不会把前面的读写指令重排序影响到volatile写,同时也不会把后面的指令放在内存屏障前面。同时保证在执行到内存屏障这条指令的时候,前面的操作都是已经完成的。
CPU是如何保证原子性的?
1.加总线锁。通过Lock#指令锁住总线,防止其他处理器同时修改内存。
2.加缓存锁定。通过缓存一致性让其他cpu的缓存数据失效。
java中如何实现原子性
1.使用CAS
2.使用重量级锁,但是这种锁的获取和释放还是使用CAS,因为是需要进入队列的。重量级锁也有同步队列。
volatile在单例模式中的应用
private valitle static Instance instance;
public static Instance getInstance(){
if (instance == null){
synchronized(Instance.class){
if (instance == null) {
instance = new Instance(); //首先这步骤是复合操作
}
}
}
return instance;
}
如果第一个线程进来执行到初始化这步了:
因为中间实例化的步骤是一个复合操作,相当于三个步骤
1.分配内存空间
2.初始化
3.引用指向
但是由于单线程情况下会出现指令重排序,23颠倒了,所以引用先指向了那个还没初始化的内存空间。
这个时候另一个线程进来能够看到instance非空那么就直接返回了,但是其实还没有初始化完成,所以出错。
使用volatile修饰instance就是为了防止指令冲排序,因为最后一步引用指向相当于一个volatile写操作,通过内存屏障就能够防止指令重排序了。