我跟volatile修饰对象和数组能保证其内部元素的可见性?一文的作者有类似的疑惑,按语义,volatile修饰数组时,只是保证其引用地址的可见性,可为什么给数组加了volatile之后,似乎单个数组元素也有了可见性?先贴个感觉靠谱的结论:
If Thread A reads a volatile variable, then all variables visible to Thread A when reading the volatile variable will also be re-read from main memory.1
In this case, it seems that all the data from the main memory will be updated back to the CPU cache when a volatile variable is read by the thread.1
《Java并发编程实战》:
按照上面的结论,读取一个无关的volatile变量,也会影响此线程的其它变量的可见性。虽然并不建议程序依赖这种可见性。我写了些代码来验证,与本博----如何理解synchronized的可见性?类似的,初始代码如下:
public class Visibility {
public static boolean b = true;
public static void main(String[] args) {
new Thread(() -> {
try {Thread.sleep(100);} catch (InterruptedException e) {} // 先让线程2启动并缓存变量b
b = false; // 如果变量b不是volatile,这里修改后,仍然对其它线程("线程2")不可见
}, "线程1").start();
new Thread(() ->{
while (b) { // 线程2就使用缓存中的b变量
}
System.out.println("get b update");
}, "线程2").start(); // 线程死循环
}
}
程序跑很久都不结束,通过debug可以看到线程2一直卡在执行while (b) 那一行。可见虽然非volatilie变量b在线程1中被修改了,但仍对线程2不可见。
将上述代码稍加改造后,程序就可以正常结束了:
public class Visibility2 {
public static boolean b = true;
public static volatile int[] ints = new int[5];
public static void main(String[] args) {
new Thread(() -> {
try {Thread.sleep(500);} catch (InterruptedException e) {} // 先让线程2启动并缓存变量
System.out.println("ready to update b");
b = false;
}, "线程1").start();
new Thread(() ->{
while (b) {
if (ints[0] == 2 || ints[0] != 2) {
}
}
System.out.println("get b update");
}, "线程2").start();
}
}
改造的关键无非就是线程中读volatile数组的某元素,它影响了变量b在线程2中的可见性。所以结论是验证通过的。
stackoverflow ↩︎ ↩︎