给我翻译翻译,什tmd叫volatile
在Happends-before原则中,对一个volatile的写必happend-before于随后对这个volatile的读,翻译翻译就是后面的volatile读的代码必须等前面的volatile写代码完成之后才能读。
你以为惊喜到这就结束了?请问volatile的内存语义和读写语义,你又知道多少呢?
三张图告诉你什么是内存屏障(Memory Barriers / Fences)
你可能会在某些文章中看到这样的一段结论:
1. 在每一个volatile写前面加一个storeStore屏障
2. 在每一个volatile写后面加一个loadStore屏障
3. 在每一个volatile读后面加一个loadStore
4. 在每一个volatile读后面加一个loadLoad
复制代码
看到这一段理论你是不是懵逼了?很好,下面我就用三段代码+三张图告诉你这段鬼话说什么是什么。
第一段代码+图(在每一个volatile写前面加一个storeStore屏障)
public class VolatileBarrierExample {
//普通变量
private int normal;
private int i = 10;
private int j = 0;
//两个volatile变量
private volatile int v1 = 1;
private volatile int v2 = 2;
/**
* 在volatile写之前的操作
*/
public void volatileWriteBefore() {
j = i;//普通读
j = j + 1;//普通写
/****试想一下如果这里不增加屏障会怎样?****/
v1 = j + 1;//volatile写
}
}
复制代码
可以看到,这段代码在volatile写之前增加了一些普通读写的操作,试想一下,如果普通读/写都到volatile写之后,数据会是什么样的结果呢?所以由此我们可以得出第一个内存屏障:在volatile写之前在StoreStore屏障,避免volatile写时候拿到的数据是正常的。
第二段代码+图(在每一个volatile写后面加一个storeLoad屏障)
public class VolatileBarrierExample {
//普通变量
private int normal;
private int i = 10;
private int j = 0;
//两个volatile变量
private volatile int v1 = 1;
private volatile int v2 = 2;
/**
* 在volatile进行写之后的操作
*/
public void volatileWriteAfter(){
v2=v2+1;//volatile写
/****试想一下如果这里不增加屏障会怎样?****/
i=v2;//volatile读
j=v2+1;//volatile写
}
}
复制代码
可以看到只有在volatile写之后价格storeload才能使得后续指令不会错乱,进而保证后续的volatile读写数据正常。
第三段代码+图(在每一个volatile读后面加一个storeLoad屏障)
public class VolatileBarrierExample {
//普通变量
private int normal;
private int i = 10;
private int j = 0;
//两个volatile变量
private volatile int v1 = 1;
private volatile int v2 = 2;
/**
* 在volatile读之后的操作
*/
public void volatileReadAfter(){
i=v2;//volatile读
/****试想一下如果这里不增加屏障会怎样?****/
i=i+1;//普通写
j=i;//普通读
v2=i+1;//volatile写
}
}
复制代码
有了上面的基础我想这段代码的意思大家都很明白吧?说白了就是"等我读完了你们才能用
再聊一聊volatile先写再读的内存语义
public class ReorderExample {
private int x = 0;
private int y = 1;
private /*volatile*/ boolean flag = false;
public void writter() {
x = 42; //代码1
y = 50; //代码2
flag = true;//代码3
}
public void reader() {
if (flag) {//代码4
System.out.println("x=" + x + " y=" + y);//代码5
}
}
}
复制代码
如上代码,假设我们有两个线程,第一个线程先对volatile变量进行写操作,第二个线程对volatile变量进行读操作。这时候线程之间的可见性是如何实现的呢?实际上在某一个线程对volatile变量进行读操作时,JMM会将该线程对应本地变量设置会无效,让其去主存中读取。是不是听着有点懵逼?一张图带你搞定这个过程。
如图所示,当线程1对volatile变量flag进行写操作后,会通知所有线程这变量修改了,你们用的都是没用的数据,当线程2收到该请求且需要读取该数据时,就会去主存中读取了,表面像看起来就像上面说的"JMM会将该线程对应本地变量设置会无效,让其去主存中读取"




近期评论