一、并发编程中的三个概念

1.原子性

原子性很好理解,这在数据库事务中常常也被提及,指一个操作,要么执行完毕且不被打断,要么就不执行。不具备原子性的操作,在并发编程中会出现非常多的奇怪现象。

2.可见性

可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值,简单地说就是某个共享变量被某个线程修改,其他使用该共享变量的线程会发现该修改并读取新值。

3.有序性

有序性即程序执行的顺序按照代码的先后顺序执行。
JVM为了提高程序运行效率,会对我们的代码进行指令重排序(Instruction Reorder),它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会根据程序语句间的数据依赖性,保证程序最终执行结果和代码顺序执行的结果是一致的。(这个结论只对单线程的程序代码有效)

二、JAVA内存模型

JAVA内存模型定义了程序中变量的访问规则,为了获得较好的执行性能,JAVA内存模型并没有限制执行引擎使用处理器的寄存器或者高速缓存来提升指令执行速度,也没有限制编译器对指令进行重排序,在java内存模型中,也会存在缓存一致性问题和指令重排序的问题。

当程序在运行过程中,会将运算需要的数据从主存复制一份到CPU的高速缓存当中,那么CPU进行计算时就可以直接从它的高速缓存读取数据和向其中写入数据,当运算结束之后,再将高速缓存中的数据刷新到主存当中。
如果一个变量在多个CPU中都存在缓存(一般在多线程编程时才会出现),当多个线程都在对该变量进行操作,就会出现缓存一致性错误。
当线程执行下面的代码时,会先从主存当中读取i的值,然后复制一份到高速缓存当中,然后CPU执行指令对i进行加1操作,然后将数据写入高速缓存,最后将高速缓存中i最新的值刷新到主存当中。
以多核CPU为例,线程可能运行于不同的CPU中,假设 i 初始值为0,有两个线程运行下面的代码,我们希望结果为 2 ,但可能存在下面一种情况:
两个线程分别读取 i 的值存入各自所在的CPU的高速缓存当中,然后线程一进行加 1 操作,然后把 i 的最新值 1 写入到内存。此时线程二的高速缓存当中 i 的值还是0,进行加 1 操作之后,i 的值为 1 ,然后线程二把 i 的值写入内存,最终 i=2。这就是缓存一致性错误。

  1. i = i + 1;