线程不安全方法,取同一个内存区数据,取到同一个数据是为什么

今天通过视频学习线程并发,测试的时候发现线程不安全的方法,取数据时,会取到大量相同的数据。我知道线程不安全可能会取到错误的数据,但是为什么会发生上述情况呢,是因为cpu么。求大神简单解释原理

/*模拟网络12306抢票概念流程*/
 public class SleepDemo02 {

    public static void main(String[] args) {
        //真实角色
        web12306 web=new web12306();
        //代理角色
        Thread t1=new Thread(web,"大黄牛");
        Thread t2=new Thread(web,"小黄牛");
        Thread t3=new Thread(web,"老黄牛");
        t1.start();
        t2.start();
        t3.start();
    }

}
class web12306 implements Runnable{

    private int num=50;
    @Override
    public void run() {

    while(true){
        if(num<=0){
            break;
        }
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"抢到了"+num--);
    } 
    }

}

关键运行结果:
老黄牛抢到了50
小黄牛抢到了49
大黄牛抢到了50
小黄牛抢到了48
老黄牛抢到了48
大黄牛抢到了47
小黄牛抢到了46
老黄牛抢到了46
大黄牛抢到了45
老黄牛抢到了44
小黄牛抢到了43
大黄牛抢到了42
老黄牛抢到了41
小黄牛抢到了40
大黄牛抢到了39
小黄牛抢到了38
老黄牛抢到了37
大黄牛抢到了36
小黄牛抢到了35
老黄牛抢到了35
大黄牛抢到了34
小黄牛抢到了33
老黄牛抢到了33
......

 private volatile int num=50;

主要是寄存器优化,你不加volatile,当多线程去读取这个变量的时候,会从寄存器中读取这个值给你,而它可能是一个旧值,也就是其他线程修改了,你不会马上读取到。而加了volatile就会强制要求每次都去读取这个变量的值,而不是从寄存器优化

 num--
其实是
临时变量  = num
临时变量 = 临时变量 - 1
num = 临时变量
当两个线程同时访问num,而不同步的情况下,可能出现这样的情况
假设num=50
A线程
临时变量  = num
临时变量 = 临时变量 - 1
此时临时变量是49,但是num还是50
B线程此时执行
临时变量  = num
临时变量 = 临时变量 - 1
然后A线程写回num = 临时变量,num是49
然后B线程写回num = 临时变量,num还是49
于是本来应该是48变成49了。