可见性问题复现
线程1不会停止,因为线程2改变了a的值,线程1不知道
public class Test {
public static Integer a = 1;
public static void main(String[] args) {
// 线程1 根本停不下来
new Thread(() -> {
while (a == 1){
}
}).start();
// 线程2 2秒后修改 a 的值
new Thread(() -> {
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2));
a = 2;
}).start();
}
}
但是如果这样写线程1就会停下来,没搞明白...(至少上面的代码证明了可见性的问题)
public class Test {
public static Integer a = 1;
public static void main(String[] args) {
new Thread(() -> {
while (a == 1){
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(500));
System.out.println("此时变量还是1");
}
}).start();
new Thread(() -> {
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2));
a = 2;
}).start();
}
}
第一段代码中,如果变量使用 volatile 修饰,线程1 就会停下来
volatile
可以保证变量的可见性、有序性
可见性
可以理解为,每个线程不是读取自己的工作内存,而是直接读取主内存,每次修改都会回写主内存,更深入的原因是缓存一致性,官方描述如下(不是很理解,后面再看):
当CPU写数据时,如果发现操作的变量是共享变量,即在其他CPU中也存在该变量的副本,会发出信号通知其他CPU将该变量的缓存行置为无效状态,因此当其他CPU需要读取这个变量时,发现自己缓存中缓存该变量的缓存行是无效的,那么它就会从内存重新读取
有序性
java 代码最终翻译成可执行的机器指令会被优化、或者只能保证赋值操作啥的是准确的,但是不一定就是我们写的代码的顺序,这就是 指令重排序,底层使用内存屏障来完成的,对于 volatile 完整的情况应该是这样的(不是很理解,后面再看)
- 在每一个volatile的写(store)之前,加入一个StoreStore屏障和一个LoadStore屏障
- 在每一个volatile的写(store)之后,加入一个StoreLoad屏障和一个StroeStore屏障
- 在每一个volatile的读(load)之后,加一个LoadLoad屏障和LoadStrore屏障