可见性
重排序与失效数据
由于多线程的情况下,某些线程有时候会将一些值缓存起来,读这些缓存而不从主内存中读取所以可能会有问题,它一直在读取一个已经失效的数值这就会导致一些问题(比如下面代码的死循环,还有比如没有线程安全get、set方法阔能读取到更新的值也阔能没读取到,那就很难顶了)。而且一些指令可能会重排序,让结果不像我们预期的那样。
尝试下面这个程序大概率会发现,陷入死循环。
public class Main {
private static int number;
private static boolean flag;
private static class ReadThread extends Thread{
@Override
public void run() {
while (!flag){};
System.out.println(Thread.currentThread().getName()+number);
System.out.println(Thread.currentThread().getName()+"输出");
}
}
public static void main(String[] args) throws InterruptedException {
new ReadThread().start();
Thread.sleep(10);
new Thread(()->{
flag =true;
number = 99;
}).start();
}
}
非原子操作的64位操作
由于Java虚拟机会将非volatile类型的long和double变量(64位),的读操作和写操作分解为两个32位操作。这会带来一些安全性的问题(比如可能会读取到更新前的高32位和其他线程更新后的低32位之类的)
解决方式
单单使用锁是没有办法解决这种局面的!!那么我们咋办呢!
synchronized+volatile
volatile的含义就说确保将变量的更新操作通知到其他线程,一个变量被声明为volatile类型后,便会告知JVM这个变量是共享变量,就不会被缓存在寄存器或者其他处理器不可见的地方,总是返回最新的写入值。这个关键字相当于范围更小的synchronized操作,读取volatile相当于进入同步代码块,写入则相当于退出,不过比锁要脆弱。
使用它必要条件为:
- 对变量的写入操作不依赖与当前值(i++这种就不行)。
- 该变量不会与其他状态变量一起纳入不变性条件中(俺理解的是原子性的范围)。
- 访问变量时不需要加锁。
且不要滥用它,简化代码实现以及同步策略时才使用,它的作用主要为:
- 确保自身状态的可见性
- 确保所引用对象状态的可见性
- 表示一些重要的程序生命周期事件的发送
private volatile static int number;
private volatile static boolean flag;