2.3.4 貌似串行语义
JIT编译器,处理器,存储器子系统是按照一定的规则对指令,内存操作的结果进行重排序,给单线程程序造成一种假象---指令是按照源码的顺苏执行的,这种假象称为貌似串行语义。并不能保证多线程环境下程序的正确性。
为了保证貌似串行语义,有数据依赖关系的语句不会被重排序,只有不存在数据依赖关系的语句才会被重排序。如果两个操作(指令)访问同一个变量,且其中一个操作(指令)为写操作,那么这两个操作之间流存在数据依赖关系。
存在控制依赖关系的语句允许重排序,一条语句(指令)的执行结果会决定另一个语句(指令)能否被执行,这两条语句(指令)存在控制依赖关系。如在if语句中允许重排序,可能存在处理器先执行if代码块,再判断if条件是否成立
2.3.5 保证内存访问的顺序
我们可以使用volatile关键字,synchronized关键字实现有序性
2.4java内存模型
-
每个线程都有独立的栈空间
-
每个线程都可以访问堆内存
-
计算机的CPU不直接从主内存读取数据,CPU读取数据时,先把主内存的数据读到Cache缓存中,把Cache中的数据读到Register寄存器中
-
JVM中的共享数据可能会被分配到Register寄存器群中,每个CPU都有自己的register寄存器。一个CPU不能读取其他CPU寄存器中的内容,如果两个线程分别运行在不同的处理器(CPU)上,而这个共享的数据被分配到寄存器上,会产生可见性问题。
-
即使JVM的共享数据分配到主内存中,也不能保证数据的可见性。CPU不直接对内存访问,而是通过Cache高速缓存进行的。一个处理器上运行的线程对数据的更新可能只是更新处理器的写缓冲器(Store Buffer),还没有到达Cache缓存,更不用说内容了。另外一个处理器不能读取到处理器写缓冲器上的内容,会产生运行在另一个处理器上的线程无法看到该处理器对共享数据的更新
-
一个处理器的Cache不能直接读取另外一个处理器的Cache,但是一个处理器可以通过缓存一致性协议(Cache Coherence Protocol)来读取其他处理器缓存中的数据,并将读取的数据更新到该处理器的Cache中这个过程称为缓存同步,缓存同步使得一个处理器上运行的线程可以读取到另外一个处理器上运行的线程对共享数据的所做的更新,即保障了可见性,为了保障可见性必须使一个处理器对共享数据的更新最终被写入该处理器的Cache,这个过程称为冲刷处理器缓存