Volatile 变量可以确保先行关系,即写操作会发生在后续的读操作之前, 但它并不
能保证原子性。例如用 volatile 修饰 count 变量那么 count++ 操作就不是原子
性的。
而 AtomicInteger 类提供的 atomic 方法可以让这种操作具有原子性如
getAndIncrement()方法会原子性的进行增量操作把当前值加一,其它数据类型
和引用变量也可以进行相似操作。
寄存器变量:
在c语言当中可以使用寄存器变量来优化程序的性能,最常见的是在一个函数体当中,将一个常用的变量声明为寄存器变量: register int ra; 如果可能的话,编译器就会为它分配一个单独的寄存器,在整个函数执行期间对这个变量的操作全都是对这个寄存器进行操作,这时候就不用频繁地去访存了,自然就提高了性能.但是寄存器变量不是强制性的,也就是,即使你使用register关键字去声明一个变量为寄存器变量,编译器还是有可能把它作为一个普通的变量而不是寄存器变量来使用的.
易失变量:
volatile(嵌入式程序员必须掌握的),volatile最初的意思是表示汽油容易挥发,在c中的作用大概有两点
(1)表示变量是易失的,易变的.
(2)强制访存操作,防止编译器去优化,告诉编译器每次必须去内存中取值,而不是从寄存器或者缓存.
缓存一致性问题
这三者之间的交互关系如下
计算机在执行程序时,每条指令都是在CPU中执行的,而执行指令过程中,势必涉及到数据的读取和写入。由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,这时就存在一个问题,由于CPU执行速度很快,而从内存读取数据和向内存写入数据的过程跟CPU执行指令的速度比起来要慢的多,因此如果任何时候对数据的操作都要通过和内存的交互来进行,会大大降低指令执行的速度。因此在CPU里面就有了高速缓存。
也就是,当程序在运行过程中,会将运算需要的数据从主存复制一份到CPU的高速缓存当中,那么CPU进行计算时就可以直接从它的高速缓存读取数据和向其中写入数据,当运算结束之后,再将高速缓存中的数据刷新到主存当中。举个简单的例子,比如下面的这段代码:
i = i + 1;
当线程执行这个语句时,会先从主存当中读取i的值,然后复制一份到高速缓存当中,然后CPU执行指令对i进行加1操作,然后将数据写入高速缓存,最后将高速缓存中i最新的值刷新到主存当中。
这个代码在单线程中运行是没有任何问题的,但是在多线程中运行就会有问题了。在多核CPU中,每条线程可能运行于不同的CPU中,因此每个线程运行时有自己的高速缓存(对单核CPU来说,其实也会出现这种问题,只不过是以线程调度的形式来分别执行的)。本文我们以多核CPU为例。
比如同时有2个线程执行这段代码,假如初始时i的值为0,那么我们希望两个线程执行完之后i的值变为2。但是事实会是这样吗?
可能存在下面一种情况:初始时,两个线程分别读取i的值存入各自所在的CPU的高速缓存当中,然后线程1进行加1操作,然后把i的最新值1写入到内存。此时线程2的高速缓存当中i的值还是0,进行加1操作之后,i的值为1,然后线程2把i的值写入内存。
最终结果i的值是1,而不是2。这就是著名的缓存一致性问题。通常称这种被多个线程访问的变量为共享变量。