内存模型
如前文所述,现代计算机通常有两个或者更多的CPU,一些CPU还有多个核;其允许多个线程同时运行,每个CPU在某个时间片内运行其中的一个线程。在《存储管理》一节中我们介绍了计算机系统中的不同的存储类别:
每个CPU包含多个寄存器,这些寄存器本质上就是CPU内存;CPU在寄存器中执行操作的速度会比在主内存中操作快非常多。每个CPU可能还拥有CPU缓存层,CPU访问缓存层的速度比访问主内存块很多,但是却比访问寄存器要慢。计算机还包括主内存(RAM),所有的CPU都可以访问这个主内存,主内存一般都比CPU缓存大很多,但速度要比CPU缓存慢。当一个CPU需要访问主内存的时候,会把主内存中的部分数据读取到CPU缓存,甚至进一步把缓存中的部分数据读取到内部的寄存器,然后对其进行操作。当CPU需要向主内存写数据的时候,会将寄存器中的数据写入缓存,某些时候会将数据从缓存刷入主内存。无论从缓存读还是写数据,都没有必要一次性全部读出或者写入,而是仅对部分数据进行操作。
从CPU到 |
大约需要的CPU周期 |
大约需要的时间 |
主存 |
- |
约60-80ns |
QPI总线传输(between sockets, not drawn) |
- |
约20ns |
L3 cache |
约40-45 cycles |
约15ns |
L2 cache |
约10 cycles |
约3ns |
L1 cache |
约3-4 cycles |
约1ns |
寄存器 |
1 cycle |
- |
并发编程中的问题,往往源于缓存导致的可见性问题、线程切换导致的原子性问题以及编译优化带来的有序性问题。以Java虚拟机为例,每个线程都拥有一个属于自己的线程栈(调用栈),随着线程代码的执行,调用栈会随之改变。线程栈中包含每个正在执行的方法的局部变量。每个线程只能访问属于自己的栈。调用栈中的局部变量,只有创建这个栈的线程才可以访问,其他线程都不能访问。即使两个线程在执行一段相同的代码,这两个线程也会在属于各自的线程栈中创建局部变量。因此,每个线程拥有属于自己的局部变量。所有基本类型的局部变量全部存放在线程栈中,对其他线程不可见。一个线程可以把基本类型拷贝到其他线程,但是不能共享给其他线程,而无论哪个线程创建的对象都存放在堆中。
在内存模型中我们可能会涉及到如下相关的概念术语: