并发bug产生的源头
在程序的运行中,需要不断使用到CPU,内存和IO设备(输入/输出设备,就是指可以与计算机进行数据传输的硬件),但是在计算机发展的过程中,这三者的速度始终存在差异,其中他们的速度比较是CPU>内存>>io。
由于程序在跑的过程中频繁需要使用CPU,内存和IO,所以单一提升其中一个操作的速度,无法使整体程序变快,程序运行速度取决于速度最慢的操作。
为了合理利用CPU的高性能,平衡三者的速度差异,各个部分都做出了相应的优化,但是优化了速度,却带来了编程中出现的bug。
设备优化带来可见性,原子性,有序性问题
- CPU缓存:产生了可见性问题
定义:一个线程对变量的修改,对于另一个线程来说是可见的,称为可见性。
CPU增加了缓存(CPU缓存:L1,L2,L3),去均衡与内存的速度差异(当程序发出内存访问请求时,CPU会先查询缓存中是否存在请求数据,如果有就直接返回,没有的话就将内存中的数据加载进缓存中并且返回处理器。具体内容参考:CPU缓存)
当两个线程去操作不同CPU上面的变量时候,CPU1上的缓存对CPU2上的缓存不可见,所以线程1对CPU1上的变量V进行修改,线程2无法立刻获取到线程1对变量V的修改,导致可见性问题的产生(如下图)。
- 分时复用:产生了原子性问题
定义:一个或多个操作在CPU执行的过程中不被中断,称为原子性。
当一个操作系统运行的过程中,进程(或线程)运行了一小段时间,操作系统就会重新选取一个新的进程(或线程)来运行,我们将这种行为称为任务切换,运行的这一小段时间,我们称为时间片。
在操作系统中增加线程与进程,以分时复用(同步分时复用,异步分时复用)CPU,从而均衡CPU与IO之间的性能差距。具体原理:在IO请求中,存在读和写两种操作,而在通讯中,等待是必然的。也就是说明,IO操作中有时候是忙碌的,有时候是空闲的,而在空闲的时候CPU可以使用分时复用去操作其他IO操作,从而减少等待IO的时间。也就是说,当一个进程(或线程)把自己标记成空闲状态时候,会交出CPU的使用权给其他进程(或线程),等待进程(或线程)被重新唤醒之后会重新获得CPU使用权。
JAVA高并发基于多线程操作,所以在程序运行过程中,发生任务切换是必然的现象。任务切换发生在时间片结束的时候,在高级语言里,一句代码需要多条CPU指令来完成,例如当i++的时候,CPU至少需要3条指令完成。
-
- 将i从内存加载到cpu寄存器上。
-
- 进行i+1操作。
-
- 将i的值写入内存或CPU缓存中。
而在操作系统中,发生任务切换的时刻可以是任意一个CPU指令的结束而非高级语言中的一行代码。 我们启动两个线程分别执行i++时候,假设i=0,当线程1进行完指令1的时候发生了任务切换,切换到了线程2,而线程2在执行过程中没有发生任务切换,执行完i+1操作后将i=1回写到内存或cpu缓存中,此时线程2运行结束切换至线程1,线程1将寄存器中的i=0执行了指令2,指令3之后,将i=1回写到内存或cpu缓存中。因此,两条线程执行完之后我们发现i=1而不是=2,具体执行流程如下所示。
- 编译优化:产生了有序性的问题
定义:程序按照代码先后顺序执行,称为有序性。
有时候,编译器为了优化执行速度,经常会改变程序的执行顺序,虽然最终结果不会变,但是在调整程序的执行顺序过程中,可能会因为任务切换导致发生bug。
在Java里,有一个双重检测的饿汉式单例模式,其代码如下:
public class Singleton {
static Singleton instance;
static Singleton getInstance(){
if (instance == null) {
synchronized(Singleton.class) {
if (instance == null)
instance = new Singleton();
}
}
return instance;
}
}
在new Singleton()的时候,我们认为的执行顺序是:
- 1.分配一块内存
- 2.在分配的内存上初始化singleton对象
- 3.将分配的内存地址赋值给instance 变量 但是实际的执行顺序是:
- 1.分配一块内存
- 2.将分配的内存地址赋值给instance 变量
- 3.在分配的内存上初始化singleton对象
如果执行完指令2的时候发生了任务切换,另一个线程来获取instance变量,由于内存地址已经赋值给instance变量,所以将直接返回instance,而此时的instance未实例化,使用未实例化的instance去获取singleton的其他成员变量,将导致空指针问题出现,这是执行优化所带来的有序性问题。
参考文献
王宝令《Java并发编程实战》
汀雨笔记《干货,肝了一周的CPU缓存基础》juejin.cn/post/693224…