1、什么是内存可见性问题?
(1)实例
要明白什么是内存可见性,我们首先来看一段代码
java
public class demo1 {
public static int isQuit = 0;
public static void main(String[] args) {
Thread thread1 = new Thread(()->{
while (isQuit == 0){
}
System.out.println("t1线程结束");
});
thread1.start();
Thread thread2 = new Thread(()->{
Scanner scanner = new Scanner(System.in);
isQuit = scanner.nextInt();
System.out.println("isQuit已被修改");
});
thread2.start();
}
}
运行结果如下
打开jconsole,查看thread1状态,发现thread1还在运行状态,也就是说thread1中的while循环还一直在继续
为什么会这样呢?
(2)内存可见性问题
在计算机中,CPU在读取寄存器时比读取内存时快很多,这时编译器会对代码进行自动优化
在上述代码中,由于while循环中没有代码,线程1不断地读取isQuit的值进行判断,操作过于频繁
因此,编译器在第一次读取isQuit的值后,便将其存放在寄存器中,后续不再读取
导致我们在线程2里对isQuit 的值进行了修改,线程1也不能察觉
由此便产生了内存可见性问题
2、volatile
要解决内存可见性问题,我们首先想到的是使用Java中的关键字volatile
在变量前加上关键字volatile修饰,变量便不会再被编译器优化,进而就不会产生内存可见性问题
java
public volatile static int isQuit = 0;
完整代码如下
java
import java.util.Scanner;
public class demo2 {
public volatile static int isQuit = 0;
public static void main(String[] args) {
Thread thread1 = new Thread(()->{
while (isQuit == 0){
}
System.out.println("t1线程结束");
});
thread1.start();
Thread thread2 = new Thread(()->{
Scanner scanner = new Scanner(System.in);
isQuit = scanner.nextInt();
System.out.println("isQuit已被修改");
});
thread2.start();
}
}
最终运行结果
可见,内存可见性问题可用volatile关键字来解决
3、其他解决办法
之所以产生内存可见性问题,是由于读取操作太过于频繁。只要我们降低读取的频率,同样也可以解决内存可见性问题
如在线程1中的while循环中加上sleep操作
java
import java.util.Scanner;
public class demo3 {
public static int isQuit = 0;
public static void main(String[] args) {
Thread thread1 = new Thread(()->{
while (isQuit == 0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("t1线程结束");
});
thread1.start();
Thread thread2 = new Thread(()->{
Scanner scanner = new Scanner(System.in);
isQuit = scanner.nextInt();
System.out.println("isQuit已被修改");
});
thread2.start();
}
}
运行结果
volatile解决指令重排序问题