在没用同步的情况下共享变量
public class MyVolatite {
private static boolean ready;
private static int number;
private static class ReaderThread extends Thread {
@Override
public void run() {
System.out.println("线程开始执行" + ready);
while (!ready) {
Thread.yield();
}
//正常这里会输出42,但是发生重排序之后,ready = true;反而先执行,从而导致这里输出0
//我自己测试了好多遍,这种情况不容易出现
System.out.println(number);
}
}
public static void main(String[] args) throws InterruptedException {
new ReaderThread().start();
number = 42;
ready = true;
}
}
运行结果:
线程开始执行true
42
这里可能会发生两种情况(发生概率很低,我在测试的时候执行了很多次都没有出现)
1、ReaderThread可能会一直执行,因为读线程可能永远都看不到ready的值
2、System.out.println(number);可能输出0(这种称作重排序)
number = 42;
ready = true;
可能会发生重排序、首先执行ready=true;
备注(重排序):
在没有同步的情况下,编译器、处理器以及运行时等都有可能对操作的执行顺序进行一些意向不到的调整。在缺乏足够同步的多线程程序中,想要对内存操作的执行顺序进行判断,几乎无法得出正确的结论; |
解决重排序:使用内置锁可以确保以一种可预测的方式执行
volatile:修饰变量,该变量上的操作与不会与其他内存操作一起重排序、适合于一写多读场景
使用volatile解决上面出现的循环执行问题,将ready变量设置为volatile类型,while (!ready) {每次判断的时候都会获取到内存中最新的ready值,所以这里就不会发生死循环
public class MyVolatite {
private volatile static boolean ready;
private static int number;
private static class ReaderThread extends Thread {
@Override
public void run() {
System.out.println("线程开始执行" + ready);
while (!ready) {
Thread.yield();
}
//正常这里会输出42,但是发生重排序之后,ready = true;反而先执行,从而导致这里输出0
//我自己测试了好多遍,这种情况不容易出现
System.out.println(number);
}
}
public static void main(String[] args) throws InterruptedException {
new ReaderThread().start();
number = 42;
ready = true;
}
}
下面是一个错误的使用volatile的示例,多个线程写,造成数据混乱
注:两个线程同时修改volatile变量,导致结果产生了偏差;所以volatile只能保证可见性(每次读到的是最新值),不能保证原子性
public class MyNotSafe {
private static volatile int sum = 0 ;
private void incrSum(){
for (int i = 0; i < 1000; i++) {
sum++;
}
System.out.println(sum);
}
public static void main(String[] args) {
MyNotSafe myNotSafe = new MyNotSafe();
Thread t1 = new Thread(()->myNotSafe.incrSum());
Thread t2= new Thread(()->myNotSafe.incrSum());
t1.start();
t2.start();
}
}
图解volatile可见性
https://www.cnblogs.com/bbgs-xc/p/12725426.html