volatile

看过八股的都知道volatile可以保证有序性和可见性,但其实他还保证了部分原子性

有序性

简单地说就是防止指令重排,保证代码按照程序员的意愿执行

它的实现是通过内存屏障来实现的。硬件层面有Load Barrier读屏障和Store Barrier写屏障 。而jvm层面有

LoadLoad(读读屏障),LoadStore(读写屏障),StoreStore(写写屏障),StoreLoad(读写屏障)。

内存屏障相当于一堵墙,墙两边的指令不能翻墙重排,而墙同侧的可以发生重排

eg:指令1 指令2 | 指令3 指令4

指令2和指令3不可以重排,即强制执行完指令2后才会执行指令3

指令1和指令2可以重排

内存屏障到底是什么?

本质上就是CPU指令

可见性

一个线程修改了volatile变量,这个修改对于其他线程立即可见

因为CPU运算速度要比内存块很多,所以会把主存的值缓存到高速缓存中。而这时线程的高速缓存可能就会

出现和主存不一致的情况。而volatile就可以解决这种情况

当写一个volatile变量时,写操作完成后会多出一条Lock为前缀的汇编指令,这指令在多核处理器下会做两件

1.将当前CPU缓存里的值写回主存

2.将其他CPU核的内存置为无效,读取必须去主存读

当读一个volatile变量时,会直接去主存里读

这样就可以保证其变量在多线程下的可见性了

部分原子性

关键字: volatile详解

它的原子性体现在赋值层面,在30位的操作系统,cpu只能一次性读写32位的数据,那么对于long类型的,也就只能分为两步,即高32位,低32位。如果在多线程中,一个线程只操作了前部分,而另外一个线程来操作这个变量,这样的话就发生了错误了么。那如果是被volatile修饰就不会发生这个问题。但自增自减就无法保证其原子性,因为这些操作都不是由单条字节码指令组成的

双重检验锁volatile真正的作用?

DCL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class TestSingleton implements Serializable {
private static TestSingleton INSTANCE;
/**
* 防止反射多个对象
*/
private TestSingleton(){
if (INSTANCE!=null) {
throw new RuntimeException("singleton");
}else{
INSTANCE=this;
}
}
public static TestSingleton getInstance(){
if (INSTANCE==null) {
synchronized(TestSingleton.class){
if (INSTANCE==null) {
INSTANCE=new TestSingleton();
}
}
}
return INSTANCE;
}
/**
* 防止序列化多个对象
* @return
*/
private Object readResolve(){
return INSTANCE;
}
}

网上有很多说法说对它是能防止new操作的指令重排序,然后并不是的,new操作分为三步(分配内存,对象初始化,指针指向),而volatile只能保证单个指令的可见性,有序性,原子性,i++这种也是无法保证的。它真正的作用是通过在new操作前加入了store_store屏障,在后加入store_load屏障。保证对INSTANCE的写是要优先于对它的读的,但其new的内部是无法保证有序性的,从而避免了有线程获得还没有初始化的实例