文章目录

  • 什么是线程不安全
  • 1、原子性
  • 2、可见性
  • 3、指令重排(编译器优化)
  • 4、cup的抢占式执行(万恶之源)
  • 5、多个线程同时操作了一个变量


什么是线程不安全

线程不安全就是线程执行的结果与我们预期的结果不一致。比如一个计算方法,正确的答案是+XXX,而最终的执行结果却是-XXX,这样就是线程不安全的。

造成线程不安全的主要有以下五个原因:

1、原子性

比如的 n++,其实是由三步操作组成的:

1. 从内存把数据读到 CPU
2. 进行数据更新
3. 把数据写回到 CPU

如果一个线程正在对一个变量操作,中途其他线程插入进来了,如果这个操作被打断了,结果就可能是错误的

2、可见性

java多线程导致系统崩溃 java多线程为什么不安全_java多线程导致系统崩溃


为了提高效率,JVM在执行过程中,会尽可能的将数据在工作内存中执行,如果工作内存找不到数据,再到共享缓存,内存去找,处理完成后分别保存在自己线程缓存,与共享缓存中,下次直接在自己的缓存找,这样会造成一个问题,共享变量在多线程之间不能及时看到改变后结果,这个就是可见性问题。

比如线程1,线程2同时对共享变量n=0进行操作,线程1进行加1操作线程2进行减1操作,线程1,执行完后,n=1被写入工作缓存和共享缓存(因为第一次线程1的工作缓存没有值)。然后线程2开始执行,先取数,得到-1,计算完后,n=0被写入工作缓存和共享缓存(因为第一次线程1的工作缓存没有值),即共享缓存最终存的是0,但在下次计算时。线程1直接在自己的缓存取值,取到的值为1,一看就是是错误的。

3、指令重排(编译器优化)

一段代码是这样的:

  1. 去前台取下 U 盘
  2. 去教室写 10 分钟作业
  3. 去前台取下快递
    如果是在单线程情况下,JVM、CPU指令集会对其进行优化,比如,按 1->3->2的方式执行,也是没问题,可以少跑一次前台。这种叫做指令重排序。但在多线程场景下就有问题了,可能快递是在你写作业的10分钟内被另一个线程放过来的(即取快递和U盘一起,这时快递还没有到前台,没有你的快递)如果指令重排序了,代码就会是错误的。

4、cup的抢占式执行(万恶之源)

这是一个不可控的原因,CPU调度哪一个线程是不能够确定的,随机的。也就是我们不知道具体的那个线程先执行,那个线程后执行。

5、多个线程同时操作了一个变量

java多线程导致系统崩溃 java多线程为什么不安全_缓存_02


单线程时结果应该为0,也是正确的结果,但上图的结果却是-1。结果是不正确的。