1、什么是内存可见性问题?
(1)实例
要明白什么是内存可见性,我们首先来看一段代码
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修饰,变量便不会再被编译器优化,进而就不会产生内存可见性问题
public volatile static int isQuit = 0;
完整代码如下
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操作
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();}
}
运行结果