章节目录
- 1.volatile 的特性
- 为什么volatile修饰变量的写操作不是原子性的?
- 2.volatile 写-读建立的 happens-before 关系
- 3.volatile 写-读的内存语义
1.volatile 的特性
首先应该明确的一点是:当声明共享变量为volatile后,对这个变量的读/写(分为单元素读写,与复合写操作)。不同的读写模式下,volatile变量对写操作的原子性体现是不一样的。
理解volatile特性的一个好办法是把对volatile变量的单个读/写,看成是同一个锁对这些单个读/写操作做了同步,示例代码如下所示:
class VolatileFeaturesExample {
volatile long vl = 0L;
public void set(long l){
vl = l;
}
public void getAndIncrement() {
vl++;
}
public long get(){
return vl;
}
}
假设有多个线程调用上述程序中的3个方法,这个程序语义和下面的程序语义等价
class VolatileFeaturesExample {
long vl = 0L;
public synchronized void set(long l){
vl = l;
}
public void getAndIncrement() {
long temp = get();
temp +=1L;
set(temp);
}
public syntronized long get(){
return vl;
}
}
解释
- 如这两个程序所示,一个volatile 变量的单个读/写操作,与一个普通变量的读/写操作都是使用同一个锁来同步,他们之间的执行效果相同。
- 锁的happens-before规则(保证前一个操作结果对后一个操作立即可见)保证释放锁和获取锁的两个线程之间的内存可见性。这很好的解释了对于一个volatile变量的读,总能看到任意线程对这个volatile变量最后的写入。
- 锁的语义决定了临界代码区的执行具有原子性。这意味着,即使是64位的long型和double型变量,只要它是volatile变量,对该变量的单个读/写就具有原子性,如果是多个volatile操作,或这是volatile++这种复合操作,这些操作在整体上是不具有原子性的。
volatile 自身特性:
1.内存可见性:对一个volatile变量的读,总能看到任意线程对这个volatile变量
最后的写入(内存可见性保证)
2.原子性:对任意单个volatile变量的单独的读写操作,都具有原子性。但对于
volatile++这种复合操作不具有原子性。
2.volatile 写-读建立的 happens-before 关系
JMM基于共享内存模型实现线程之间的通信
从jdk1.5 开始 volatile 变量的写-读可以实现线程之间的通信。
volatile & synchronized内存语义
从内存语义来说,volatile的写-读与锁的释放-获取有相同的内存效果:volatile
写和锁释放有相同的内存语义;volatile读与锁的获取有相同的内存语义。
下面为volatile变量示例代码:
class VolatileExample {
int a = 0;//其实是线程共享变量
volatile boolean flag = false;
public void writer(){
a = 1; //1
flag = true; //2
}
public void reader(){
if(flage){ //3
int i = a; //4
......
}
}
}
假设线程A 执行 writer() 方法之后,线程B 执行reader() 方法。根据happens-before 规则,这个过程建立的happens-before规则可以分为三类:
1.根据程序次序规则,1 happens before 2;3 happens before 4.
2.根据volatile规则,2 happens-before 3
3.根据happens-before c传递性规则 1 happens-before 4
图形化形势如下图所示:
A线程在写一个volatile变量后,B线程读同一个volatile变量。A线程在写volatile之前所有的可见共享变量,在B线程读到同一个volatile变量之后,将立即变得对B线程可见。
4. volatile 写-读的内存语义
** volatile 写内存语义 **
当写一个volatile变量时,JMM会把该线程对应的本地内存的变量中的变量值强制刷新到主内存。
volatile 读的内存语义
当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。
volatile内存语义 总结
- 线程A 写一个volatile 变量,实质上是线程A 向接下来读取这个volatile变量的某个线程发出了(其对共享变量所做修改的)消息。
- 线程B 读一个volatile 变量,实质上是线程B 接收了之前某个线程发出的(在写这个volatile变量之前对共享变量所做修改)消息。
- 线程A 写一个volatile 变量,随后线程B读这个volatile变量。这个过程实质上是线程A通过主内存向线程B发送消息。