多线程一:并发问题的起源

一:CPU,内存,I/O设备的速度差异

这些年,我们的 CPU、内存、I/O 设备都在不断迭代,不断朝着更快的方向努力。但是,在这个快速发展的过程中,有一个核心矛盾一直存在,就是这三者的速度差异。
CPU 和内存的速度差异可以形象地描述为:CPU 是天上一天,内存是地上一年(
假设 CPU 执行一条普通指令需要一天,那么 CPU 读写内存得等待一年的时间)。
内存和 I/O 设备的速度差异就更大了,内存是天上一天,I/O 设备是地上十年。

二:系统的优化

为合理利用CPU的高性能,平衡三者之间的速度差异,计算机体系结构,操作系统,编译器都做出了贡献:

1. CUP增加了缓存,以均衡与内存的速度差异;

2. 操作系统增加了进程,线程,以分时复用CUP,进而均衡CPU与IO设备的速度差异;

3. 编译器优化指令执行次序,使得缓存能够更加合理的利用,提高执行效率;

三:系统优化带来的并发问题三大源头

1. 缓存导致的可见性问题

可见性:一个线程对共享变量的修改,另一个线程能够立即看到,我们称之为可见性。

多核CPU时代,每个CPU都有自己的高速缓存,计算时先把数据加载到自己的缓存中,计算完成后再刷新回主内存,很显然多核CPU操作共享变量时,就不具备了可见性。
例如:假设i是共享变量,线程A B同时执行循环10000次i++的运算,我们期望得到的结果是20,但实际可能得到的却是 10-20中间的随机数,这是因为线程A B
第一次都将i=0加载到各自的CPU缓存中,经过第一次运算得到的结果都是1,导致刷新到内存中的值为1而不是我们期望的2。这就是缓存的可见性问题。

2. 线程切换带来的原子性问题

我们把一个或者多个操作在 CPU 执行的过程中不被中断的特性称为原子性。
现代操作系统都是基于多进程和多线程实现的,CPU时间片结束的时候会发生线程切换,出让CPU的使用权,等待下一次时间片的到来。
如果切换的时机是某一计算操作只完成了一半,另外的线程又基于此结果进行下一次计算,就会造成计算错误的问题。这就是原子性问题。
例如:高级语言里一条语句往往需要多条 CPU 指令完成,i+=1;至少需要三条CPU指令才能完成
    指令1:把变量i从内存加载到CPU的寄存器中
    指令2:执行+1操作
    指令3:把结果写入内存中

线程A B 同时执行i+=1操作,线程A执行完指令2,发生线程切换,线程B执行指令1-3,线程A再次得到时间片执行指令3,
则两次计算得到的最后结果都是1,而不是我们期望的2。

3. 编译器指令重排序带来的有序性问题

有序性指的是程序按照代码的先后顺序执行,编译器为了优化性能,有时候会改变程序中语句的执行顺序。
例如:在java领域有一个使用双重检查来创建单例对象的经典做法,在获取实例 getInstance() 的方法中,
我们首先判断 instance 是否为空,如果为空,则锁定 Singleton.class 并再次检查 instance 是否为空,
如果还为空则创建 Singleton 的一个实例。
假设有两个线程 A、B 同时调用 getInstance() 方法,他们会同时发现 instance == null ,于是同时对 
Singleton.class 加锁,此时 JVM 保证只有一个线程能够加锁成功(假设是线程 A),另外一个线程则会
处于等待状态(假设是线程 B);线程 A 会创建一个 Singleton 实例,之后释放锁,锁释放后,线程 B 被唤醒,
线程 B 再次尝试加锁,此时是可以加锁成功的,加锁成功后,线程 B 检查 instance == null 时会发现,已经创建过 Singleton 实例了,
所以线程 B 不会再创建一个 Singleton 实例。
看上去是非常完美的,无懈可击,但实际上这个 getInstance() 方法并不完美。
问题出在哪里呢?出在 new 操作上,我们以为的 new 操作应该是:

1:分配一块内存M
2:在M上初始化对象
3:把M的地址赋给变量instance

但实际经过编译器优化后,很可能是这样的:

1:分配一块内存M
2:把M的地址赋给变量instance
3:在M上初始化对象
假设线程A先执行getInstance()方法,执行完指令2发生了线程切换,此时线程B执行此方法,判断instance != null 直接返回instance,
但此时instance没有初始化过,可能发生空指针异常。

问题1:

在32位的机器上对Long型数据进行加减操作存在并发隐患,这是什么原因呢?
Long型数据是64位的,在32位的机器上加减操作通常需要多条指令组合出来,无法保证原子性。

 

 

 
 
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值