一、什么是内存可见性
内存可见性是在编译器优化的背景下,一个线程修改了变量而另一个线程却没有感知到修改。举个例子,一个线程一直频繁的读取变量n并将n值与某一值进行比较,在底层这个操作对应着两个指令:读取内存中的n值加载到寄存器 LOAD 然后进行比较 CMP 这两个操作前者所需时间远远比后者多,短时间内重复进行LOAD、CMP操作性能低,编译器发现多次读取n值,该n值并没有被修改于是后续的读取不从主内存中读取n(内存)而是从工作内存中读取缓存的n值进行比较,这个时候另一个线程修改了内存里的n值,之前那一个线程并不能感知到
static int count = 0;
public static void main(String[] args) {
new Thread(){
@Override
public void run(){
while(count==0){
}
}
}.start();
new Thread(()->{
try {
Thread.sleep(2000);
Scanner scanner = new Scanner(System.in);
count = scanner.nextInt();
System.out.println("count修改完成");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
当我们运行代码后输入一个值将count修改,此时发现线程1并没有结束
二、如何避免内存可见性
我们可以通过给可能存在内存可见性的属性加上volatile关键字,该关键字可以告诉编译器每次都需要去读取内存里的值不进行编译器优化
volatile static int count = 0;
public static void main(String[] args) {
new Thread(){
@Override
public void run(){
while(count==0){
}
}
}.start();
new Thread(()->{
try {
Thread.sleep(2000);
Scanner scanner = new Scanner(System.in);
count = scanner.nextInt();
System.out.println("count修改完成");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
这样当输入值修改完count后,线程1会终止