package jmm;
/**
* 描述: 演示可见性带来的问题
*/
public class FieldVisibilityMe1 {
volatile int a = 1;
volatile int b = 2;
private void change() {
a = 3;
b = a;
}
private void print() {
System.out.println("a=" + a + ";b=" + b);
}
public static void main(String[] args) {
while (true) {
FieldVisibilityMe1 test = new FieldVisibilityMe1();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
test.change();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
test.print();
}
}).start();
}
}
}
结果:

分析:
加了volatile关键字之后,依旧会出现a=1;b=3的情况不是volatile关键期不字作用,没有把修改后的值立刻刷新到主内存。而是会出现以下一种情况。
读线程取a的值的时候,依旧是会去主内存拷贝a的值到工作内存,但是由于写线程还没修改a的值,取到的值还是1。当读线程要取b的值的时候,写线程已经完成了b的赋值操作,当读线程取主内存拷贝b的值的时候,拷贝的是修改后的值3。所以就出现了a=1;b=3的情况。
那假如print函数中的
System.out.println("a=" + a + ";b=" + b);
改成System.out.println("b=" + b + ";a=" + a);
呢?结果是不会出现b=3;a=1的情况了。
由于读线程去主内存拷贝b的值,此时b必然是已经修改后的值,然后再去主内存拷贝a的值,由于a必在b之前修改,所以a必然是已经修改后的值。所以就不会出现b=3;a=1的情况了。
那假如在上述情况下再改变一下,a不是用volatile关键字修饰会怎么样?
结果也是不会出现b=3;a=1的情况了。
由于as-if-series原则保证单线程执行结果的一致性,a=3必在b=a之前执行,再根据happens-before原则的传递性,a=3对b=a可见,b=a对读线程可见,所以a=3对读线程可见。所以就不会出现b=3;a=1的情况了。
网友评论