欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【Java进阶知识】多线程导致的不安全现象

程序员文章站 2022-05-05 22:48:50
...

在我们设计多线程时,要考虑的问题不外乎就两个——安全、效率!
效率很好理解,那么对于安全呢?多线程为什么会不安全?这篇博客总结自己对多线程安全的初步讲解

1.多线程体现出来的不安全

如果现在让我们给定一个类的静态变量COUNT,并且创建20个线程,在每个线程中都对该变量进行+10000操作,那么,在执行完这个整个操作后,我们期望COUNT返回的值应该是20,0000 那是不是就会这样书写代码:

public class UnsafeThread {
    public static int COUNT;    //静态变量大写,基本数据类型在初始化出来后都有默认值
    public static void main(String[] args) {
   //开启20个线程,每个线程对COUNT进行++操作10000次,预期结果200000
        for(int i = 0; i < 20; i++){
             new Thread(new Runnable() {
               @Override
               public void run() {
                   for(int j = 0; j < 10000; j++){
                       COUNT ++;
                   }
               }
           }).start();
        }
        while(Thread.activeCount() > 2){
            Thread.yield();
        }
        System.out.println(COUNT);
     }
}

执行代码,我们得到的结果是20,0000吗?
事实上并不是!并且每次执行的结果都不相同,但都比20,0000小,显然,我们的结果是有问题的

造成这一现象的原因就是线程的不安全导致的!因为我们创建出的20个线程相互之间是平行的,它们的执行也是不分先后的。事实上每次的COUNT++操作都要经历三个步骤:

  1. 从主存上读取到COUNT当前的值
  2. 对COUNT 进行+1操作
  3. 将修改后的COUNT值重新写回主存中

而分为三个步骤就导致一个很大的问题:假设线程1正在进行第二步,对COUNT修改时,此时线程2也开始执行此代码块,当它从主存读取数据时,读到的是线程1未修改之前的数据,对此数据进行操作。此时就导致对COUNT操作不同步,使得很多线程对COUNT的操作最终无效。

所以,简单的来说如果多线程环境下代码运行的结果是符合我们预期的,即在单线程环境应该的结果,则说这个程序是线程安全的,否则,它就是不安全的。

2.不安全的原因

(1)原子性:

为了理解原子性我们把一段代码想象成一个房间,每个线程就是要进入这个房间的人。如果没有任何机制保证,A进入房间之后,还没有出来;B 是不是也可以进入房间,打断 A 在房间里的隐私。这个就是不具备原子性的。
那我们应该如何解决这个问题呢?是不是只要给房间加一把锁,A 进去就把门锁上,其他人是不是就进不来了。这样就保证了这段代码的原子性了。
有时也把这个现象叫做同步互斥,表示操作是互相排斥的。

当然要注意,一条 java 语句不一定是原子的,也不一定只是一条指令。比如刚才我们看到的 n++,其实是由三步操作组成的。那么不保证原子性会给多线程带来什么问题呢?显然,就如同上面的示例一样,如果一个线程正在对一个变量操作,中途其他线程插入进来了,如果这个操作被打断了,结果就可能是错误的。

(2)可见性
主内存-工作内存

【Java进阶知识】多线程导致的不安全现象
如图所示,为了提高效率,JVM在执行过程中,会尽可能的将数据在工作内存中执行,但这样会造成一个问题,共享变量在多线之间不能及时看到改变,这就是可见性。

(3)代码顺序性
什么是代码重排序?

例如,有这样一系列操作:

  1. 去前台取下 U 盘
  2. 去教室写 10 分钟作业
  3. 去前台取下快递

如果是在单线程情况下,JVM、CPU指令集会对其进行优化,比如,按 1->3->2的方式执行,也是没问题,可以少跑一次前台,这种就叫做指令重排序。
显然单线程情况是没问题的,但在多线程场景下就有问题了,什么问题呢。可能快递是在你写作业的10分钟内被另一个线程放过来的,或者被人变过了,如果指令重排序了,代码就会是错误的。

当然,解决多线程不安全也有很多方法,我们以后逐渐学习。