一、可见性
1、实例讲解
先看这样一段代码:
public class Test {
static boolean a = true;
public static void main(String[] args) {
a = false; //对a执行写操作
System.out.println(a); //对a执行读操作
}
}
我们在单线程中,对a执行了写操作,并且读取到了最新写的值,也就是说,单线程中对a的写操作时可见的。
那么我们再开启一个线程 :
public class Test {
static boolean a = true;
public static void main(String[] args) throws InterruptedException {
new Thread(()-> {
while(a) {} //死循环
}).start();
Thread.sleep(1000); //为了保证不会影响,停一秒再写
a = false; //对a执行写操作
System.out.println(a); //对a执行读操作
}
}
可以看到,虽然a仍然打印出为false,但是程序没有结束,就说明在我们新开启的线程中a的值始终为true,他才可以一直执行while循环。换句话说,我们的主线程对a的写操作对于新开的线程的读操作来说是不可见的。
为什么这么长时间了,新开线程中的a还是true呢?那是因为新线程中一直在执行循环,使得线程没有机会去拿到主存中a的最新值,而是一直读取缓存中a的值。
那么,我们让循环沉睡一会儿,给他去读最新值的机会:
public class Test {
static boolean a = true;
public static void main(String[] args) throws InterruptedException {
new Thread(()-> {
while(a) {
try {
Thread.sleep(1); //睡1ms,给线程去读新值的机会
} catch (InterruptedException e) {
e.printStackTrace();
}
} //死循环
}).start();
Thread.sleep(1000); //为了保证不会影响,停一秒再写
a = false; //对a执行写操作
System.out.println(a); //对a执行读操作
}
}
可以看到,程序很快就结束了,说明新线程已经读到了a的新值为false,结束了循环。
2、如何理解Java线程中的不可见性?
简单来说:线程1读,线程2写,而线程1读不到线程2写的值,这就是不可见性。
3、那么如何实现可见性呢?
就需要用到volatile关键字了:
public class Test {
static volatile boolean a = true;
public static void main(String[] args) throws InterruptedException {
new Thread(()-> {
while(a) {} //死循环
}).start();
Thread.sleep(1000); //为了保证不会影响,停一秒再写
a = false; //对a执行写操作
System.out.println(a); //对a执行读操作
}
}