结论:volatile修饰对象或数组时,只能保证他们的引用地址的可见性。
非volatile数组的可见性问题
public class Test {
static int[] a = new int[]{1};
public static void main(String[] args) {
new Thread(() -> { //线程A
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
a[0] = 0;
}).start();
new Thread(()-> { //线程B
try {
while (true) {
if (a[0] == 0) {
System.out.println("结束");
break;
}
//Thread.sleep(10);
}
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
由此可见打印不出来‘’结束‘’
volatile数组的可见性问题
public class Test {
static volatile int[] a = new int[]{1};
public static void main(String[] args) {
new Thread(() -> { //线程A
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
a[0] = 0;
}).start();
new Thread(()-> { //线程B
try {
while (true) {
if (a[0] == 0) {
System.out.println("结束");
break;
}
//Thread.sleep(10);
}
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
发现结果和我们预期的相反
当ThreadB读取array时,因为array的引用被volatile修饰,所以ThreadB对所有变量都会从主内存去获取,当然也就包括array[0]。
https://stackoverflow.com/questions/53753792/java-volatile-array-my-test-results-do-not-match-the-expectations
其实如果不给数组加volatile就永远不会打印“结束”,这种绝对的认为是错误的,volatile保证了其变量及时可见性而不会引起线程安全的问题,就算不加volatile,cpu在空闲的时候也会将array[0]的值从线程缓存刷新到主存,只是while(true)太快了,导致cpu一直没空。我们可以使用Thread.sleep(10)让出CPU让其有空将线程变量刷新到主内存中去。记得先去掉volatile修饰
public class Test {
static int[] a = new int[]{1};
public static void main(String[] args) {
new Thread(() -> { //线程A
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
a[0] = 0;
}).start();
new Thread(()-> { //线程B
try {
while (true) {
if (a[0] == 0) {
System.out.println("结束");
break;
}
Thread.sleep(10);
}
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}