为什么多线程下会有线程安全问题

原子性:加锁(乐观锁CAS,悲观锁)

原子性是指一个操作或一系列操作要么全部执行成功并且不被中断,要么完全不执行,没有中间状态。在多线程或并发环境下,如果一个操作是原子性的,那么其他线程不会在该操作执行过程中看到该操作的部分结果。原子性是为了保证操作的一致性和正确性。例如,一个转账操作,涉及从一个账户扣除一定金额并将其添加到另一个账户,这个操作应该是原子的,否则可能导致数据不一致的情况。

可见性:voliate,加锁(乐观锁CAS,悲观锁)

可见性是指当一个线程对共享变量进行了修改后,其他线程能够立即看到这个修改。在多线程环境中,每个线程都有自己的缓存,如果一个线程修改了共享变量,其他线程可能并不立即知道这个变化,而是需要一定的机制来保证修改对其他线程可见。可见性问题可能导致线程之间的数据不一致性和错误行为。解决可见性问题的常见方法是使用同步机制,如锁或volatile变量,来确保变量的修改对其他线程是可见的。

线程出现线程安全问题的原因(主要原因)

1、线程在系统调度中顺序无序,抢占式执行,谁先抢到谁执行

2、多线程修改同一个变量,修改操作不是原子(不可分割的最小单位)的,如++操作就涉及到CPU的三个指令:

load 加载,add 增加,save 保存。如果两个线程对同一个变量进行操作时,会有很多排列顺序,就会造成变量值计算的最终结果不是预期结果。

3、内存可见性:在多线程环境下,编译器对于代码的优化,产生误判(某个变量明明在其它线程执行过程中更改了,但是当前线程不知道),所以会引起bug。比如假设线程A的中断条件为某个变量flag=1才可以中断(默认flag = 0 不满足线程A中断条件),线程B在执行过程中将flag=1(满足线程A中断条件),但是线程A没有休眠,执行速度极快,编译器就会对代码进行优化(只加载一次flag的值),当线程B将flag=1时,线程A并不会重新加载flag的值,所以就会造成线程A无法中断,从而bug出现。

4、指令重排序:完成某个任务的结果一样,但是过程顺序不一致。在多线程情况下,可能会使结果不可预知,从而产生bug。(很难调试出来,完全靠自己去想去理解)这里给大家举个例子:

我们实例化某个对象时,new操作主要分为三步:

1) 创建出对象(建好房子)

2) 构造对象(装修房子)

3) 将生成的地址赋值给对象引用(拿到钥匙)。

多线程情况下,线程调度无序,那么某个线程可能会拿到一个没有构造好的对象(啥也没有,属性都是默认的),那么我们去使用该对象成员变量或方法时,可能就会发生一系列的错误。

posted @ 2023-07-23 18:04  yifanSJ  阅读(48)  评论(0编辑  收藏  举报